Home > Enterprise >  Angular HttpStatus 4xx | 5xx Interceptor (conditional)
Angular HttpStatus 4xx | 5xx Interceptor (conditional)

Time:01-10

In an Angular v.11 (also v.13 ) I need to write an Interceptor to show an error-text message to the user when the backend request return an error (status code 4xx or 5xx).

I know it is possible in Angular to write HttpInterceptors for this case, but I have a special case, where I must intercept and show the error-text message only if no explicit handlig for http-errors specified.

ex. code:

Case-1:

this.http.get().subscribe(
   success(data),
   error(err)
);

Case-2:

this.http.get().subscribe(
   success(data)
);

For clarification, I need to show the error-text message only if there is no error(err) handling function defined (like in Case-2).

Im not sure how to do that, and I am not sure if that is possible, but I think there should be a simple solution to that problem, that I cannot find by myself.

CodePudding user response:

Sounds like global error handling that is only applied if there's no local error handling. It isn't exactly the type of architecture I'd go for, tbh.

The global error handling would somehow need to know when to apply its handling based on provided parameters or some other global service it could check (ie. local error handling would need to notify a service).

I can't judge how much of your error handling you can change, but global error handling should handle generic errors that apply to a majority if not all HTTP requests. While the local one would handle specific cases exclusive to that particular request. That way you could run both error handlers without the risk of them interfering with each other.

Since your problem seems to be UI related (ie. displaying error msg), the above approach would cover that as well. Global error handling would display some sort of generic error msg while you could add an additional error msg for your local handler. Both would be utilizing the same service for displaying error messages, I suppose.

CodePudding user response:

Ignoring if its a good approach or not and focusing on the feasibility. Our goal is to only launch a generic handler when no other error handling is provided.

First let us understand better the tools to available to us, the HttpClient and the HttpInterceptor.

  • HttpClient methods: create a HttpRequest based on the provided options and methods, attempts to resolve it and returns an Observable.
  • HttpInterceptor: it receives an HttpRequest and based on it performs something. It must return an Observable<HttpEvent<any>>.

There is only one point connecting our 2 elements, the options that are used to create the HttpRequest that will be used by our HttpInterceptor. Therefor, we cannot access the .subscribe() to check if there is an error handling callback or not.

In short, with just adding the error handling callback, it is not possible to perform what you are asking with the provided restrictions.

The question then arrises, can we achieve it if we somehow lax our restrictions? Well the answer lies in the options we pass to our HttpClient method.

We could pass an unique param to our interceptor, and use it to check if it needs the global error handler or not, like so:

  withGlobalError(): void {
    this.httpClient.get('www.foo.com', { params: { globalError: true } }).subscribe(
      _ => console.log('success')
    );
  }

  withoutGlobalError(): void {
    this.httpClient.get('www.foo.com').subscribe(
      _ => console.log('success'),
      error => console.log('Error handler in local scope', error)
    );
  }

In our HttpInterceptor we need to check if the param globalError exists and is true. When so we can perform whatever we need.

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    return next.handle(request).pipe(catchError(error => {
      if (request.params.get('globalError')) {
        console.log('Handling error in the Global Interceptor');
      }
      return throwError(error);
    }))
  }

This is the simplest approach to achieve what you want. It allows for global error handling to exist, while at the same time ensuring that every developer is aware of when it will be triggered.

CodePudding user response:

You can add a simple flag to the header when using HttpClient, then in the HTTP_INTERCEPTORS, you can check if this flag is existing, to decide to handle it or not!

For e.g. :-

//In component or service file: -

this.http.get(
  'https://jsonplaceholder.typicode.com/todos/2',
  { headers: {'byPass': 'yes'} })
  .subscribe((response: any) => {
    console.log(response);
  }
);

//In interceptor: -

public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // if there is byPass header, don't handle the error and remove this flag here
    if (req.headers.has('byPass')) {
        const newHeaders = req.headers.delete('byPass')
        const newRequest = req.clone({ headers: newHeaders });
        return next.handle(newRequest);
    } else {
        //Handle the error accordingly
        return next.handle(req);
    }
}

Still you should write a generic Error handler in interceptor only.

  • Related