Search code examples
angularerror-handlingangular-http-interceptors

HttpInterceptor - want server responses to flow to my component for certain error conditions


I would like error responses, in the response body, from my API server to flow all the way to my component for certain error codes, for example 400 or 422. It works well without an HttpInterceptor in the following implementation.

function  handleHttpError() {
  return (response: any) => {
    let newResponse;
    if (response.status === 0) {
      // Error connecting to the server
      newResponse = { error: 'Error connecting to the server!' }
    } else {
      // Server returns a response in response.error
      newResponse = response.error;
    }
    return of(newResponse);
  };
}

export class MyService {
  ...

  saveTransaction(trans: Transaction): Observable<APIResponseBody> {
    return this.http.post<APIResponseBody>(this.transUrl, trans)
      .pipe(catchError(handleHttpError()));
  }

  ...
}

export class MyComponent {

  ...

  onSubmit() {
    this.myService.saveTransaction(this.data) {
      .subscribe((res: APIResponseBody) => {
        if (res.error) {
          this.toastService.showError(`${res.error}: ${JSON.stringify(res.attributes)}`);
        } else if (res.trans) {
          this.trans = res.trans;
        }
      })
    }
  }
  ...

}

It does not, however, work when I move the error handling to HttpInterceptor. All non-2xx http errors seem to be filtered by the interceptor in the following implementation:

export class HttpInterceptorService implements HttpInterceptor {
  constructor() { }
  intercept (request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((response: any) => {
          let newResponse;
          if (response.status === 0) {
            // Error connecting to the server
            newResponse = { error: 'Error connecting to the server!' }
          } else {
            // Server returns a response in response.error
            newResponse = response.error;
          }
          return of(newResponse);
        })
      );
  }
  }
}
  
export class MyService {
  ...
  
  saveTransaction(trans: Transaction): Observable<APIResponseBody> {
    return this.http.post<APIResponseBody>(this.transUrl, trans);
  }
  
  ...
}

What am I missing in my interceptor implementation?


Solution

  • The interceptor needs to return an Observable<HttpEvent>, in your case an Observable<HttpResponse>

    So instead of returning the newResponse object directly you just need create a new HttpResponse and set your response as the body of the response.

    export class HttpInterceptorService implements HttpInterceptor {
      constructor() { }
      intercept (request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request)
          .pipe(
            catchError((response: any) => {
              ...
    
              return of(new HttpResponse({ body: newResponse }));
    
            })
        );
      }
    }
    

    cheers