In a recent project, the API server exposed a custom response header called request-id, which uniquely identified the network request. If the network request failed, the request id could be used to find its respective entry in the error log. However, the typical end user wouldn’t know how to provide this information when reporting an issue. Let’s learn how to use an Angular Interceptor to capture a custom response header!
HttpInterceptor
The HttpInterceptor interface provides a means of intercepting HTTP requests and responses to transform or handle them before passing them along.
Let’s create a service that implements the HttpInterceptor interface.
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError, filter } from 'rxjs/operators';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request);
}
}
For my particular scenario, I was only interested in HTTP responses, specifically internal server errors (HTTP status code of 500). We’ll add a pipe and make use of the filter and catchError operators.
return next.handle(request).pipe(
filter(event => event instanceof HttpResponse),
catchError(error => {
if(error.status === 500) {
const requestId = error.headers.get('request-id');
...
}
return throwError(error);
})
);
We filter the entries specific to HTTP responses and then catch any errors that occur. We check for the error status of 500 and then capture the request-id header value.
You could then pass this information off to another service or maybe store it in local storage. Create a routine to display this information to the end user and they can include it when reporting an issue!
Provide the Interceptor
In order to actually use the interceptor, you have to define it as a provider in whatever module you want to use it in. In my particular use case, I provided it in the app module so I could capture errors across the entire application.
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ErrorInterceptor } from '/path/to/interceptor';
@NgModule({
..
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
]
..
})
export class AppModule {}
You can use multiple interceptors, but keep in mind that Angular provides them in the order that you provide them. For example, if you provide interceptors A, then B, then C, requests will flow in A > B > C and responses will flow out C > B > A.