Djaty - How to capture 404 errors when clicking on a corrupted route inside my angular application? - djaty

I've checked the docs and found that there are some ways to ignore a certain error but what I want to do is the opposite of this. I want to include an error that is ignored by default. For example, 404 errors when clicking on a corrupted route inside my angular application or when landing on a route that doesn't exist.
Note: I don't want to catch 404 Ajax requests. I want to catch the errors when navigating to a route - an Angular route- that doesn't exist.

By default, angular will throw an unhandled exception for non existing routes. So, make sure that Djaty.trackBug(exception) is added to your error handler. If you don't register the ErrorHandler to your AppModule, you can create it as the following:
export class DjatyErrorHandler implements ErrorHandler {
handleError(err: Error): void {
// Calling `trackBug` to notify Djaty about all uncaught exceptions.
Djaty.trackBug(err);
}
}
And add it to AppModule providers
#NgModule({
// ...
providers: [{provide: ErrorHandler, useClass: DjatyErrorHandler}],
// ...
})
export class AppModule { }
If you are not interested in tracking any uncaught exception - Which is not recommended - you can subscribe to router.events and call Djaty.trackBug when the event is a NavigationError
Ex:
Add this code to app.component.ts
this.router.events.subscribe(ev => {
if (ev instanceof NavigationError) {
Djaty.trackBug(ev.error);
}
});

Related

FST_ERR_REP_ALREADY_SENT when trying to sendFile on Nestjs notfoundExecption

I want to serve spa app with nestjs and fastify, it works but when I refresh a 404 is triggered. So I wrote an exception to catch the 404 and send the file but I keep getting FST_ERR_REP_ALREADY_SENT error.
My code below
import { Catch, ExceptionFilter, ArgumentsHost, HttpException, NotFoundException } from '#nestjs/common';
import { FastifyReply } from 'fastify';
#Catch(NotFoundException)
export class NotFoundExceptionFilter implements ExceptionFilter {
catch(_exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse() as FastifyReply;
response.sendFile('index.html');
}
}
I want 404 to serve the index.html file
I have had this issue in the past and an update from Nest 8 to 9.x fixed the bug. #Cavdy you may want to update to the latest Nest, atleast anything >=9 and you should be good to go.
It's probably catching something on notFoundHandler before it reaches your exception filters. I recommend setting it on notFoundHandler instead of using exception filters reference

Calling an onCall function in Firebase Functions using AngularFire results in a 404 error, while returning the expected result

I have created a few onCall cloud functions using Firebase. These functions interact with Stripe through the API. When I use AngularFire, or more specifically, AngularFireFunctions to call these said cloud functions, I receive the error message A bad HTTP response code (404) was received when fetching the script. in Chrome developer console. Yet, the expected result is received with no problem and the Firebase console displays a 200 response with no error messages or warnings. The project is entirely hosted on Firebase.
The 404 error also does not display a file that it is connected to in the console as such errors typically do within that console.
UPDATE
I also feel it is relevant to include, the Stripe developer logs in the dashboard reflect no errors, but a successfull call upon checking.
I have also tried to remove the call to Stripe in the cloud function and simply only return a string return 'The customer ID is:'+ ${data.customerId}+'. Thank you.' and still received the same error message.
I have also tried this solution, https://github.com/angular/angularfire/issues/1933#issuecomment-432910986 with the following code being placed inside app.module.ts however, am unable to find where FunctionsRegionToken would be defined to be able to import it.
providers: [
{ provide: FunctionsRegionToken, useValue: 'us-central1' }
]
Although, I'm not sure how changing the region to the same region the function is being called from currently would make any difference.
When you explore the Network tab of the developer console and visit the page that calls the function, you see that something is trying to call http://localhost:4200/firebase-messaging-sw.js which doesn't exist. The amount of calls to this file and the errors in the console coincide with each other which leads me to believe they are related.
END OF UPDATE
I have tried to add CORS to my cloud function (and am using it in onRequest functions), I've tried rewriting my cloud function, and even tried changing the client side function that calls the onCall to no avail. The only way to remove the error is to remove the call to the function, thus I've narrowed it down to something with the AngularFireFunctions.
What I am using and the versions
Angular V13
Firebase 9.6.7
Angular Fire 7.2.1
Node 16.13.1
What follows is my code, broken up into sections.
Cloud function
const cors = require('cors')({origin: true});
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
const FieldValue = require('firebase-admin').firestore.FieldValue;
admin.initializeApp();
const firebaseApp = admin.app();
const firebaseDB = firebaseApp.firestore();
const Stripe = require('stripe');
const stripe = Stripe(functions.config().stripe.key);
export const retrieveCustomer = functions.https.onCall( async(data) => {
if(data.customerId) {
const customer = await stripe.customers.retrieve(data.customerId);
if(customer) {
return customer;
} else {
throw new functions.https.HttpsError('unknown', 'An unknown error occurred, please try again.');
}
} else {
throw new functions.https.HttpsError('invalid-argument', 'A customer ID must be provided.');
}
});
Angular Service
import { Injectable } from '#angular/core';
import { AngularFirestore } from '#angular/fire/compat/firestore';
import { AngularFireFunctions } from '#angular/fire/compat/functions';
#Injectable({
providedIn: 'root'
})
export class BillingService {
constructor( private aff: AngularFireFunctions, private afs: AngularFirestore ) { }
RetrieveCustomer(customerId:string) {
const callable = this.aff.httpsCallable('retrieveCustomer');
return callable({
customerId: customerId
});
}
}
Angular Component
import { Component, OnInit, AfterContentInit } from '#angular/core';
import { BillingService } from 'src/app/shared/services/billing/billing.service';
#Component({
selector: 'app-billing-settings',
templateUrl: './billing-settings.component.html',
styleUrls: ['./billing-settings.component.css']
})
export class BillingSettingsComponent implements OnInit, AfterContentInit {
public stripeCustomer!: any;
constructor( private billingService: BillingService ) { }
ngOnInit(): void {
}
ngAfterContentInit(): void {
this.billingService.RetrieveCustomer('cus_LGRX8TPVF3Xh0w').subscribe((customer:any) => {
console.log(customer);
});
}
}

UnauthorizedException is deliver as Internal Server Error

I'm trying a create a shared Guard as an external library in order to be imported and used across services. I'm not doing anything special that what is described in some guides but with the particularity that the code will reside in a shared library. Everything is working but the Exception to return a 401 error.
My guard looks something like this:
import { Injectable } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class MainGuard extends AuthGuard('jwt') {}
Nothing else. If I use that in a service folder it works, but at the time that I move as in their own library, the response changes.
The way that I'm using in the service has nothing special:
import { MainGuard } from 'shared-guard-library';
import { Controller, Get, UseGuards } from '#nestjs/common';
import { SomeService } from './some.service';
#Controller()
export class SomeController {
constructor(private someService: SomeService) {}
#Get('/foo')
#UseGuards(MainGuard)
async getSomething(): Promise<any> {
return this.someService.getSomething();
}
}
The client receives an error 500:
http :3010/foo
HTTP/1.1 500 Internal Server Error
Connection: keep-alive
Content-Length: 52
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Dec 2021 04:11:42 GMT
ETag: W/"34-rlKccw1E+/fV8niQk4oFitDfPro"
Keep-Alive: timeout=5
Vary: Origin
X-Powered-By: Express
{
"message": "Internal server error",
"statusCode": 500
}
And in the logs shows:
[Nest] 93664 - 12/08/2021, 10:11:42 PM ERROR [ExceptionsHandler] Unauthorized
UnauthorizedException: Unauthorized
at MainGuard.handleRequest (/sharedGuardLibrary/node_modules/#nestjs/passport/dist/auth.guard.js:68:30)
at /sharedGuardLibrary/node_modules/#nestjs/passport/dist/auth.guard.js:49:128
at /sharedGuardLibrary/node_modules/#nestjs/passport/dist/auth.guard.js:86:24
at allFailed (/sharedGuardLibrary/node_modules/passport/lib/middleware/authenticate.js:101:18)
at attempt (/sharedGuardLibrary/node_modules/passport/lib/middleware/authenticate.js:174:28)
at Object.strategy.fail (/sharedGuardLibrary/node_modules/passport/lib/middleware/authenticate.js:296:9)
at Object.JwtStrategy.authenticate (/sharedGuardLibrary/node_modules/passport-jwt/lib/strategy.js:96:21)
at attempt (/sharedGuardLibrary/node_modules/passport/lib/middleware/authenticate.js:360:16)
at authenticate (/sharedGuardLibrary/node_modules/passport/lib/middleware/authenticate.js:361:7)
at /sharedGuardLibrary/node_modules/#nestjs/passport/dist/auth.guard.js:91:3
The logs are telling me that the correct exception was thrown, but is ignored at some point and I don't know the reason. Again: the same code in the same project works.
I took a look at the original class and I don't see any particular way to treat the exception
Any clue or guide it will appreciate.
So, this happens to be a "feature" of Typescript and how JavaScript object equality works in general. So in Nest's BaseExceptionFilter there's a check that exception instanceof HttpException, and normally, UnauthorizedException would be an instance of this, but because this is a library there's a few things that need to be considered.
All of the NestJS dependencies you're using have to be peerDependencies. This makes sure that when the library is installed, there's only one resulting package for the #nestjs/* package.
during local development, you'll need to take care to ensure that you're not resolving multiple instances of the same package (even if it's the exact same version, to JavaScript { hello: 'world' } === { hello: 'world' } // false). To take care of this, things like npm/yarn/pnpm link should not be used, but instead you should copy the dist and the package.json to the main application's node_modules/<package_name> directory.
a. The other option is using a monorepo tool like Nest's monorepo approach or Nx which have single package version approaches, and use the paths of the libraries rather than internal links.
If you follow this, when your production application installs the npm library, everything will work without an issue. It's an annoyance for sure, but it's a side effect of how JavaScript works
I had a similar problem with a custom auth package inside a monorepo.
My auth-library exposed AuthModule, JwtAuthGuard, and some utility functions. All needed packages were installed under my library so any other projects that were using it had not installed other versions of dependencies. Unfortunately using a custom guard caused Internal Server Error.
I've solved this issue by adding a global custom exception filter. It looks for a workaround but at least solves this issue.
This filter is exported from auth-library, so UnauthorizedException indicates on the same object as AuthGuard.
import { ArgumentsHost, Catch, ExceptionFilter, UnauthorizedException } from '#nestjs/common';
import { Response } from 'express'
#Catch(UnauthorizedException)
export class UnauthorizedExceptionFilter implements ExceptionFilter {
public catch(exception: UnauthorizedException, host: ArgumentsHost): Response {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
return response.status(401).json({ statusCode: 401 });
}
}
See Extending Guards: https://docs.nestjs.com/security/authentication#extending-guards
In most cases, using a provided AuthGuard class is sufficient. However, there might be use-cases when you would like to simply extend the default error handling or authentication logic. For this, you can extend the built-in class and override methods within a sub-class.
Implement handleRequest(err, user, info) as follows:
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}

Vue SSR serverPrefetch server redirect with this.$ssrContext.res.redirect

Is it possible to do a redirect from a component, in VUE SSR, without getting the error on the server console: [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client?
Code is as follows:
serverPrefetch() {
// need the data from this.fetchPage service to decide on the redirect
return this.fetchPage().then(function (response) {
// do some checks here
// THIS IS THE NODE EXPRESS REDIRECT
this.$ssrContext.res.redirect('/');
this.$ssrContext.res.end(); // with or without, same error
}.bind(this));
},
beforeMount() {
this.fetchPage();
}
NOTE: If I try to use this.$router.replace('/') (or any other methods), I get the error: [Vue warn]: Error in callback for watcher $route: ReferenceError: document is not defined.
In my project I did the redirect on the client side, but I was wondering if this can be done also from server.
TL;DR:
Reject the serverPrefetch with {url: '/redirect-route'}
Found the solution as follows: If you implement the vue-ssr project as the https://github.com/vuejs/vue-hackernews-2.0 repo, in server/index.js you have a handleError function that has a redirect in it:
if (err.url) {
res.redirect(err.url);
}
And all you need to do in your component is:
serverPrefetch() {
return this.fetchPage().then(res => {
if(res === 'MY_REDIRECT_CONDITION') {
// !!! THE ACTUAL REDIRECT TRIGGER !!!
return Promise.reject({url: '/'});
}
});
},

Servicestack Display 404 page CatchAllHandlers

Im using servicestack Core with kestrel. I made a CatchAllHandlers delegate with the following code.
var requestType = typeof(NotFoundPage);
var restPath = new RestPath(requestType, pathInfo);
return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
But the problem is that my ServicestackApi now is no longer reachable, url: /json/reply/GetApiCall goes to the 404 not found page.
Is there a way to solve this? can i check if its an api call or can i go later in the pipeline to handle the request?
update
I found that if i remove CatchAllHandler and just add the next middleware this middleware is called:
app.Use((context, next) =>
{
context.Response.Body.Write("yaayaya");
return Task.CompletedTask;
});
But this is not what i want, i want to stay inside the servicestack request.
update 2
Looking at the source-code i find HttpHandlerFactory has a property NotFoundHttpHandler Which is filled from the AppHost.
CustomErrorHttpHandlers.Add(HttpStatusCode.NotFound, new PageNotFoundHandler());
The only downside is that i can't provide any request specific information to this Urlhandler, such as the url itself:
public class PageNotFoundHandler : RestHandler
{
public PageNotFoundHandler()
{
var restPath = new RestPath(typeof(Error404), "/Url/For?");
}
}
Trying to make this work but i'm getting stuck on that my RestHandler has different amount of components than the url since this PageNotFoundHandler is made before the RestHandler.
But Basically what im looking for is to Handle a different service/InputDto
I've tried RequestConverters but this code is not reached when CatchAllHandlers doesn't return an Handler. so im stuck in this space in the middle. Anyway i could make all the left over routes, route to a single Dto?
.NET Core's new pipeline programming model expects you to call the next middleware if it wasn't already handled by any of the previously registered middleware which is how .NET Core lets you combine multiple different middlewares into the same App.
Handling Not Found Requests with the last Middleware
The last middleware that's registered will be able to handle any unhandled requests so for instance if you wanted to return a static image for unhandled requests you could register middleware after ServiceStack, e.g:
app.UseServiceStack(new AppHost());
app.Use(new StaticFileHandler("wwwroot/img/404.png"));
Or if you wanted to return a custom 404 page instead:
app.Use(new RazorHandler("/404"));
Which will render the /wwwroot/404.cshtml Razor View with ServiceStack's MVC Razor Views.
This would be the preferred way to handle Not Found requests in .NET Core in which you will be able to register additional middleware after ServiceStack to handle non-ServiceStack requests.
Calling a ServiceStack Service for unhandled requests
If you wanted to call a ServiceStack Service for any unhandled requests you can use a Fallback Route which matches on any request, e.g:
[FallbackRoute("/{Path*}")]
public class Error404
{
public string Path { get; set; }
}
public class UnhandledRequestService : Service
{
public object Any(Error404 request) => ...;
}

Resources