Custom global error handler not hit when http error response is re-thrown by rxjs throwError
Asked Answered
R

3

7

Aim: to have a global error handler for server errors and app errors (produced in Typescript code).

How: providing a custom ErrorHandler from a lib project inside the same workspace. This is my lib structure:

@common/error-handling lib

I have following http-interceptor (http-error.interceptor.ts)

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {

  constructor(@Inject(LOGGER_SERVICE) private logger: ILoggerService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req)
    .pipe(
        catchError( (error: HttpErrorResponse) => {
          console.log('error');

          return throwError(error);
        })
    );
  }
}

The following custom global error handler (errors-handler.ts):

import { ErrorHandler, Injectable } from '@angular/core';

@Injectable()
export class ErrorsHandler implements ErrorHandler {

    handleError(error: any): void {
        console.log('hi!');
    }

}

And this is the error-handling.module.ts

import { NgModule, ErrorHandler } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpErrorInterceptor } from './http-error.interceptor';
import { ErrorsHandler } from './errors-handler';

    @NgModule({
      declarations: [],
      imports: [
      ],
      exports: [],
      providers: [
        {provide: ErrorHandler, useClass: ErrorsHandler},
        {provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true}
      ]
    })
    export class ErrorHandlingModule { }

In my public_api.ts file I only export the module

/*
 * Public API Surface of error-handling
 */

export * from './lib/error-handling.module';

In the same workspace I have an app (the default app provided by Angular CLI, it is not inside projects folder). In my app.module.ts I have imported the ErrorHandlingModule :

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { ErrorHandlingModule } from '@common/error-handling';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { CoreModule } from './core/core.module';



@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    CoreModule,

    ErrorHandlingModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

I have already built my @common/error-handling lib. I also have a fake api with json-server in which I have defined a "categories" end point. I call that service on app.component.ts in the onInit method, producing a 404 http error response by calling to the endpoint "categorie" (without "s"). I have the following in the console when I serve my app:

Error shown in console, but not catched by global error handler

The 404 error is there, I can also see the "error" log from http-error.interceptor.ts, but I cannot see the "hi!" log from my custom ErrorHandler.

The global error handler is working when I throw an error from app.component.ts after calling the fake api endpoint. Somehow, the line

return throwError(error);

in http-error.interceptor.ts is not reaching my global error handler.

Is it something related to zone.js? The error is thrown there and not bubbled up to the rest of the app. I am not so familiar with zone.js.

Any other thoughts?

Thanks in advance!

Best, Max.

Rumney answered 28/2, 2019 at 21:38 Comment(3)
When I throw error from Interceptor ErrorHandler can not catch it why?Schall
@MuhammedOzdogan I do not understand your question. Could you please reformulate it?Rumney
You have this statement : "return throwError(error);" in HttpErrorInterceptor class. Should "ErrorsHandler" catch that?Schall
C
4
export class ErrorHandlingModule { 
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: ErrorHandlingModule,
      providers: [
        {provide: ErrorHandler, useClass: ErrorsHandler},
        {provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true} ]
    };
}

and then in the AppModule import with ErrorHandlingModule.forRoot()

For extra info - https://angular.io/api/router/RouterModule#forRoot

Chivalrous answered 28/2, 2019 at 22:34 Comment(1)
thanks for your answer. The problem was really stupid. I had a class that used as a base service (all services inherited from this base service), and there I had a catchError to return default values on error. Since this was the last catch of the chain, I was not hitting the global error handler. Sorry for wasting your time and thanks for answering again!Rumney
R
1

If your observable takes a callback for the error, the throwError operator will be returned to the observer and will not be picked up by the global error interceptor.

Just comment the catchError block in subscribe

getWorkData(id: string): void {
this.userDataService.getWorkData(id).subscribe(
  (response) => {
    this.Id = response.Id;
    this.localStorage.setItem('WorkData', response);
  }
  // (err) => {
  //   //this.monitoringService.logException(err);
  // },
);
}
Resistless answered 13/12, 2020 at 4:16 Comment(0)
K
1

I found @avramz's answer helpful. I just want to provide a more complete example of what I did.

Bootstrap code

providers: [...,
  {
    provide: HTTP_INTERCEPTORS,
    useClass: HttpErrorInterceptorService,
    multi: true,
  },
]

Service code

@Injectable({ providedIn: 'root' })
export class HttpErrorInterceptorService implements HttpInterceptor {

  constructor(
    readonly matSnackbar: MatSnackBar,
  ) {
  }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError(e => {
        this.matSnackbar.showErrorUnexpected(`Something unexpected happened (${e.status})`);
        return throwError(() => e);
      }),
    );
  }
}
Knowland answered 8/3, 2023 at 14:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.