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

Time:12-25

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?

CodePudding user response:

I think this is mostly because of not returning the correct type in your handler function, you're returning an observable of unknown type which is not the interceptor expects. Take a look at this and try it:

@Injectable()
export class ErrorHandlerInterceptor implements HttpInterceptor {

  intercept(request: HttpRequest < any > , next: HttpHandler): Observable < HttpEvent < any >> {
    return next.handle(request).pipe(catchError((error) => this.errorHandler(error)));
  }

  // Customize the default error handler here if needed
  private errorHandler(response: HttpErrorResponse): Observable < never > {
    if (!environment.production) {
      // Do something with the error
      this.logger.logError('Request error '   JSON.stringify(response));
    }

    // console.error(error);
    const httpErrorCode = response.status;
    switch (httpErrorCode) {
      case 404:
        this.router.navigateByUrl('/not-found');
        break;
      case 403:
        this.router.navigateByUrl('/forbidden');
        break;
      case 500:
      default:
        // 500 Error Handing
        break;
    }

    throw response;
  }
}

CodePudding user response:

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

CodePudding user response:

You mention 2 examples here so I would try to explain whats happening in each one.

1st example without the interceptor

I see problems with the way that you use the catchError function.

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

Inside catch error you either pass an arrow function () => { } of how it should behave or a reference to an existing function in your case handleHttpError.

So the correct way would be

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

The way that you have it as handleHttpError() is a direct execution of the method handleHttpError and it is then passed to the catchError the response of that invocation. Also the function handleHttpError you have declared does not take any parameter which is wrong.

So the first example without the interceptor is wrong and is just a coincidence that what you see matches with what you expect.

2nd example with interceptor

Let's go to the second example with the interceptor. There the arrow function that you pass in catchError is not correctly implemented and therefore it does not work.

Try it with the following

catchError((response: HttpErrorResponse) => {
      let newResponse;
      if (response.error instanceof ErrorEvent) {
           newResponse = { error: 'Error connecting to the server!' }
      } else {
          //The response object here holds 2 information
          // response.error  -> information about error
          // response.status -> information about the status code returned
          newResponse = response;
      }
      return of(newResponse);
    });
  • Related