With interception, you declare interceptors that inspect and transform HTTP requests from your application to a server. The same interceptors can also inspect and transform a server's responses on their way back to the application. Multiple interceptors form a forward-and-backward chain of request/response handlers.
Interceptors can perform a variety of implicit tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
Without interception, developers would have to implement these tasks explicitly for each HttpClient
method call.
To implement an interceptor, declare a class that implements the intercept()
method of the HttpInterceptor
interface.
Here is a do-nothing noop
interceptor that passes the request through without touching it:
import { Injectable } from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; /** Pass untouched request through to the next request handler. */ @Injectable() export class NoopInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(req); } }
The intercept
method transforms a request into an Observable
that eventually returns the HTTP response. In this sense, each interceptor is fully capable of handling the request entirely by itself.
Most interceptors inspect the request on the way in and forward the potentially altered request to the handle()
method of the next
object which implements the HttpHandler
interface.
export abstract class HttpHandler { abstract handle(req: HttpRequest<any>): Observable<HttpEvent<any>>; }
Like intercept()
, the handle()
method transforms an HTTP request into an Observable
of HttpEvents
which ultimately include the server's response. The intercept()
method could inspect that observable and alter it before returning it to the caller.
This no-op
interceptor calls next.handle()
with the original request and returns the observable without doing a thing.
next
objectThe next
object represents the next interceptor in the chain of interceptors. The final next
in the chain is the HttpClient
backend handler that sends the request to the server and receives the server's response.
Most interceptors call next.handle()
so that the request flows through to the next interceptor and, eventually, the backend handler. An interceptor could skip calling next.handle()
, short-circuit the chain, and return its own Observable
with an artificial server response.
This is a common middleware pattern found in frameworks such as Express.js.
The NoopInterceptor
is a service managed by Angular's dependency injection (DI) system. Like other services, you must provide the interceptor class before the app can use it.
Because interceptors are optional dependencies of the HttpClient
service, you must provide them in the same injector or a parent of the injector that provides HttpClient
. Interceptors provided after DI creates the HttpClient
are ignored.
This app provides HttpClient
in the app's root injector, as a side-effect of importing the HttpClientModule
in AppModule
. You should provide interceptors in AppModule
as well.
After importing the HTTP_INTERCEPTORS
injection token from @angular/common/http
, write the NoopInterceptor
provider like this:
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
Notice the multi: true
option. This required setting tells Angular that HTTP_INTERCEPTORS
is a token for a multiprovider that injects an array of values, rather than a single value.
You could add this provider directly to the providers array of the AppModule
. However, it's rather verbose and there's a good chance that you'll create more interceptors and provide them in the same way. You must also pay close attention to the order in which you provide these interceptors.
Consider creating a "barrel" file that gathers all the interceptor providers into an httpInterceptorProviders
array, starting with this first one, the NoopInterceptor
.
/* "Barrel" of Http Interceptors */ import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { NoopInterceptor } from './noop-interceptor'; /** Http interceptor providers in outside-in order */ export const httpInterceptorProviders = [ { provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true }, ];
Then import and add it to the AppModule
providers array
like this:
providers: [ httpInterceptorProviders ],
As you create new interceptors, add them to the httpInterceptorProviders
array and you won't have to revisit the AppModule
.
There are many more interceptors in the complete sample code.
Angular applies interceptors in the order that you provide them. For example, consider a situation in which you want to handle the authentication of your HTTP requests and log them before sending them to a server. To accomplish this task, you could provide an AuthInterceptor
service and then a LoggingInterceptor
service. Outgoing requests would flow from the AuthInterceptor
to the LoggingInterceptor
. Responses from these requests would flow in the other direction, from LoggingInterceptor
back to AuthInterceptor
. The following is a visual representation of the process:
The last interceptor in the process is always the
HttpBackend
that handles communication with the server.
You cannot change the order or remove interceptors later. If you need to enable and disable an interceptor dynamically, you'll have to build that capability into the interceptor itself.
Most HttpClient
methods return observables of HttpResponse<any>
. The HttpResponse
class itself is actually an event, whose type is HttpEventType.Response
. A single HTTP request can, however, generate multiple events of other types, including upload and download progress events. The methods HttpInterceptor.intercept()
and HttpHandler.handle()
return observables of HttpEvent<any>
.
Many interceptors are only concerned with the outgoing request and return the event stream from next.handle()
without modifying it. Some interceptors, however, need to examine and modify the response from next.handle()
; these operations can see all of these events in the stream.
Although interceptors are capable of modifying requests and responses, the HttpRequest
and HttpResponse
instance properties are readonly
, rendering them largely immutable. They are immutable for a good reason: An app might retry a request several times before it succeeds, which means that the interceptor chain can re-process the same request multiple times. If an interceptor could modify the original request object, the re-tried operation would start from the modified request rather than the original. Immutability ensures that interceptors see the same request for each try.
Your interceptor should return every event without modification unless it has a compelling reason to do otherwise.
TypeScript prevents you from setting HttpRequest
read-only properties.
// Typescript disallows the following assignment because req.url is readonly req.url = req.url.replace('http://', 'https://');
If you must alter a request, clone it first and modify the clone before passing it to next.handle()
. You can clone and modify the request in a single step, as shown in the following example.
// clone request and replace 'http://' with 'https://' at the same time const secureReq = req.clone({ url: req.url.replace('http://', 'https://') }); // send the cloned, "secure" request to the next handler. return next.handle(secureReq);
The clone()
method's hash argument lets you mutate specific properties of the request while copying the others.
The readonly
assignment guard can't prevent deep updates and, in particular, it can't prevent you from modifying a property of a request body object.
req.body.name = req.body.name.trim(); // bad idea!
If you must modify the request body, follow these steps.
clone()
method.// copy the body and trim whitespace from the name property const newBody = { ...body, name: body.name.trim() }; // clone request and set its body const newReq = req.clone({ body: newBody }); // send the cloned request to the next handler. return next.handle(newReq);
Sometimes you need to clear the request body rather than replace it. To do this, set the cloned request body to null
.
TIP: If you set the cloned request body to
undefined
, Angular assumes you intend to leave the body as is.
newReq = req.clone({ … }); // body not mentioned => preserve original body newReq = req.clone({ body: undefined }); // preserve original body newReq = req.clone({ body: null }); // clear the body
© 2010–2023 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://angular.io/guide/http-intercept-requests-and-responses