Search code examples
angularangular-http-interceptors

Angular interceptor is altering the pipleline for erronous HTTP responses


I was asked to modify an existing angular http interceptor, specifically to add a logic to show an error in the developer console when the request to an API fails.

After reading some articles about it, I read that using pipe combined with tap on the response I could use catchError to show it.

That part is working but it seems that the pipeline is being affected because even though I am returning an Observable of the error on the catchError function, that value is not being returned to the receiving end of this pipeline (i.e. when a subscribe is being used on an API call)

Here is the relevant code of the interceptor that I have.

What am I doing wrong that is affecting the pipeline ? why is the existing code not receiving the errors even though I am returning them.

intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
    if (!this.isRefreshingToken) {
      // not relevant logic
    }

    const reqCopy = this.addHeaderToRequest(req);

    // continue the request flow and tap into the response
    return next.handle(reqCopy).pipe(
      tap(evt => {
        if (evt instanceof HttpResponse) {
          if (evt.status === 500) {
            console.log(">>", evt.body);
          }
        }
      }),
      catchError((err: any) => {
        /* in case a special logic is neeeded for HttpErrorResponse
        if(err instanceof HttpErrorResponse) {

        }
        */
       console.error(err) ;

       if(!!err.error) {
        console.error(JSON.stringify(err.error));
       }

        // return an observable of the error as required by the pipeline
        return of(err);
      })
    );
}

Here is the code of a call to the API that used to work, meaning that the //login failed logic was executed when a error was received when calling the backend, but now that logic is not being executed, for this and for many other api calls.

this.service.login(this.model).subscribe(
  // login successful
  () => {
      //not relevant code
  },
  // login failed
  error => {
    this.ssoService.init();
    console.log("Login error: ", error);
    switch (error.originalError.status) {
      case 307: 
        // need password change
        break;
      case 400:
        this.notificationService.showError(
          NotificationMessages.LoginUserOrPasswordIncorrect
        );
        break;
      case 423:
        this.error = NotificationMessages.LoginAccountLocked;
        break;
      case 404:
        this.notificationService.showError(
          NotificationMessages.LoginUserOrPasswordIncorrect
        );
        break;
      default:
        break;
    }

Solution

  • In the intercept function , you are returning next.handle(reqCopy).pipe( , which is not a type of Observable<HttpEvent<any>>.

    you need to handle in following way.

    intercept(
    req: HttpRequest<any>,
    next: HttpHandler
    ): Observable<HttpEvent<any>> {
    
        const reqCopy = req;
    
        // continue the request flow and tap into the response
        const event = next.handle(reqCopy);
        event.pipe(
          tap(evt => {
            // ... your code
          }),
          catchError((err: any) => {
              //....... your code
            return of(err);
          })
        );
    
        return event;
    }
    

    Here is the demo - https://stackblitz.com/edit/angular-interceptors-8aygw4

    Hope this helps.