Issue
I have an Angular HttpInterceptor to catch errors and display appropriate, yet generic, error messages depending on the status code.
I have a specific case where I actually expect and error message (the UI tries to release a lock on a resource that has just been deleted, so I get 404).
In that case I want to handle the error directly where I make the API call, and skip the interceptor.
I tried this:
releaseReviewerLock(itemType: EquipmentItemType, itemId: EquipmentItem["id"]): Observable<void> {
  return this.http
    .post<void>(`${this.configUrl}/${itemType.toLowerCase()}/${itemId}/release-reviewer-lock/`, {})
    .pipe(
      catchError(e => {
        if (e.status === HttpStatusCode.NotFound) {
          // We can ignore the 404 because the item has just been deleted, so there's nothing to release.
          return EMPTY;
        }
      })
    );
}
But not only is my intercepto called anyway, the catchError block above is not executed at all (breakpoint didn't stop).
Can I achieve what I want without modifying the interceptor and keeping a resemblance of single-responsibility?
Thanks!
Solution
We can pass some metadata context to HttpClient and then retrieve it inside the HttpInterceptor.
Of course, it implies to add some logic inside HttpInterceptor, but thanks to metadata context, this code could be more versatile, and stay simple.
For example:
In api.service.ts :
this.httpClient
  .get('http://...', {
    context: new HttpContext().set(IGNORED_STATUSES, [404]),
  })
  .pipe(
    catchError((e) => {
      console.log('Error catched locally', e);
      return of(EMPTY);
    })
  )
  .subscribe();
In error.interceptor.ts :
export const IGNORED_STATUSES = new HttpContextToken<number[]>(() => []);
export class CustomHttpInterceptor implements HttpInterceptor {
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    const ignoredStatuses = req.context.get(IGNORED_STATUSES);
    return next.handle(req).pipe(
      catchError((e: HttpErrorResponse) => {
        // if ignored statuses are set
        // and returned status matched 
        if (ignoredStatuses?.includes(e.status)) {
          // rethrow error to be catched locally
          return throwError(() => e);
        }
        // process error...
        console.log('error interceptor !!', e);
        return of();
      })
    );
  }
}
Answered By - Thierry Falvo
 
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.