Angular HTTP Interceptors : Multiple Interceptors and 6 Code Examples of Interceptors
Interceptors are used in Angular to Intercept HttpRequest/HttpResponse
. We can use interceptors to log HttpRequest/HttpResponse
, add additional headers to HttpRequest
before passing it to server, make changes to request body or changing format of response received from server etc..
In this article I will explain how to use multiple interceptors and 6 most common uses of Interceptors with Example. By reading my article you will get a rough idea on overall aspect of interceptors :
I will discuss below listed topics in this article :
- How to add Multiple Interceptors ?
- Basics Steps to implement HttpInterceptor
- Examples of Interceptors
i) Authentication / Session Interceptor
ii) Request Format Interceptor
iii) AJAX animation interceptor
iv) Notify error interceptor
v) TimeStamp interceptor
vi) Retry Request Interceptor - Using all Interceptors
- Github link
- Angular guide link:
- Thanks for reading 😃 ..
How to add Multiple Interceptors ?
Multiple Interceptors can be added in Angular with each Interceptor having different purpose. We can add all the functionalities within one interceptor but it may make things messy. So, to make the application modular we create different interceptors for different purpose.
Interceptors.ts :
- import
HTTP_INTERCEPTORS
from'@angular/common/http'
- import Interceptor services.
- Add all Interceptors in an array
export const interceptorProviders = [{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }];
HTTP_INTERCEPTOR
: A multi-provider token which represents the array ofHttpInterceptor
s that are registered.
const HTTP_INTERCEPTORS: InjectionToken<HttpInterceptor[]>;
provide:
mention it as HTTP_INTERCEPTORS
useClass
: mention the class which should be added to HttpInterceptor
s array
multi:true
this is required setting which tells angular that token is multiprovider. The application will throw error if you set it to false.
app.module.ts :
- import
HttpClientModule
from'@angular/common/http'
. - import
interceptorProviders
frominterceptors.ts
. Add theInterceptorProviders
array inproviders
array within@NgModule
decorator inapp.module.ts
Basics Steps to implement HttpInterceptor
basic-interceptor.service.ts :
- import
Injectable
from'@angular/core'
and add@Injectable({providedId:'root'})
decorator to class to make it Angular Service. - import
HttpInterceptor
from'@angular/common/http'
ImplementHttpInterceptor
to make it Interceptor. - import
HttpRequest, HttpHandler, HttpEvent
from'@angular/common/http'
and implementintercept
function as our class inherits/implementsHttpInterceptor
.
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
4. intercept
function will return next.handle(req);
. It means that we are passing control to the next interceptor in the chain, if there is one.
next.handle(req);
You can also pass your own observable and short-circuit the interceptor chain and ignore the preceding interceptors and the backend handler. ex :-
return of(new HttpResponse<any>({body:'dummy body',url:'dummyurl.com'}));
Examples of Interceptors
Authentication / Session Interceptor
Authentication Interceptor can be used to add authentication header to your request. You can even route the application to login page if user is unauthenticated.
auth-interceptor.service.ts :
- Follow basic steps to implement
HttpInterceptor
- add
AuthService
dependency within constructor parameter. - check if user is authenticated using this.auth.isAuthenticated
- if user is authenticated add the authentication token to request header. here we are using
request.clone()
because request is immutable object. - if user is unauthenticated route him to login page.
Request Format Interceptor
At times we may receive response from Api server in XML format. We need to convert format of the XML data to Json to use it in application. We can use interceptor to handle such responses.
xml2-json-interceptor.service.ts :
- Follow basic steps to implement
HttpInterceptor
. - import
NgxXml2jsonService
from‘ngx-xml2json’;
(XML to JSON converter service)
You need to install the package before using it in your project.npm install ngx-xml2json --save
- Import
HttpResponse
from'@angular/common/http';
- Change the expected response type to
text
instead ofjson
. We make this change in order to receive XML as well as JSON response. If the response from server is in XML format and responsetype is'json'
we get following errorSyntaxError: Unexpected token < in JSON at position 0
. So, to avoid error we change request in following wayreq = req.clone({ responseType: 'text'});
. - Apply
map
operator tonext.handel(req)
. Note : we are usingmap
operator instead oftap
because we can only read the response usingtap
. - Within the map operator if type of event is
HttpResponse
and if type of content is'application/xml'
we convert the body of event from Xml to Json using below code :const parser = new DOMParser();
const xml = parser.parseFromString(event.body, ‘text/xml’);
event = event.clone({ body: this.xml2jsonService.xmlToJson(xml) });
We useevent.clone(..)
becauseHttpResponse
if immutable. - If type of event is
HttpResponse
and if type of content is'application/json'
we convert the body of event from text to Json usingevent = event.clone({body:JSON.parse(event.body)});
. We are converting the body from text to json because we had requested response type as text but now we need the body in Json format so we can use it in application.
AJAX animation interceptor
One of the best example of interceptor in ajax animation interceptor. This interceptor will help you display a animation in your application whenever AJAX/XHR request is made by your Angular application. Mostly we add a loading animation to Header section of page. We have 3 sections for this service to work
- Interceptor : which will notify the directive.
- Notifier Service : which will contains current busy state of API requests. If there is any ongoing API request the value passed to
busy
will be true else it will befalse
. - Directive : which will be added as an html attribute to the animation CSS or GIF which needs to be displayed when api Call occurs.
ajax-busy-identifier-interceptor.service.ts :
- Follow basic steps to implement
HttpInterceptor
. - Call
this.beginRequest();
at the beginning of intercept function. It will increment therequestCounter
or setrequestCounter
as 1 if it is less than or equal to 0. IfrequestCounter
is equal to 1 it will pass true toabns.busy
BehaviourSubject. Which notifies the ajax-busy-indicator directive that ajax request has initiated. - Call
this.endRequest()
withinfinalize(()=>{})
. It will decrement the requestCounter if it is greater than 1 else set it equal to 0.
if reequestCounter is equal to 0 we will pass false toabns.busy
BehaviourSubject. Which will notify the ajax-busy-indicator directive that all ajax requests have finished(response received or failed).
ajax-busy-notifier.service.ts :
AjaxBusyNotifierService
contains propertybusy
which is object ofBehaviorSubject<boolean>;
.BehaviorSubject<boolean>
behaves as observable as well as observer. In the interceptor it behaves as observer when it callsabns.busy.next(true)
. In Directive It will behave like observable when it callsabns.busy.subscribe((busy)=>{…})
.
ajax-busy-indicator.directive.ts :
- Add
@Directive
decoratorAjaxBusyIndicatorDirective
class to make it directive. - Add
selector:'[ajax-busy-indicator]'
option to the decorator. This will make it attribute directive. Now we can addajax-busy-indicator
to any element to apply this directive. - Declare
showDelay
andhideDelay
variable and add @Input decorator to make it input attributes for the directive. - Declare
showTimer
andhideTimer
Subscription objects so we can unsubscribe show and hide timers. cancelPendingHide()
will unsubscribe the hideTimer andcancelPendingShow()
will unsubscribe the showTimer.- Within ngOnInit we will add 2 subscribers to our
abns.busy
BehaviourSubject . One to hide the content(loading animation) and other to show the content within directive. - if the value passed to
abns.busy
is true
a) it will callcancelPendingHide()
to unsubscribe tohideTimer
.
b) it will initiate the timer with showDelay as period of timer . Within the timer it will remove'inactive'
class from directive element and then unsubscribe the timer and make timernull
. - Similarly we will do inverse if
abns.busy
is false.
Notify error interceptor
Notify error interceptor can be used to display error if response of the AJAX/XHR request is error.
error-notifier.service.ts :
- Follow basic steps to implement
HttpInterceptor
. - Import
ToastrService
fromngx-toastr
. InjectToastrService
dependency using constructor.constructor(private toasterService: ToastrService){}
. Before using ToasteService we need to install it using npm :npm install ngx-toastr — save
@angular/animations
package is a required dependency for the default toastnpm install @angular/animations --save
- Within intercept function apply
tap
operator tonext.handle(request)
Observable using pipe function. Tap the error and display it usingtoasterService
.
TimeStamp interceptor
Request timestamp interceptor service is used to log the total response time for a particular request. We also add startTime and endTime header to the response header which can be used within the application as well.
request-timestamp.service.ts :
- Follow basic steps to implement
HttpInterceptor
. - Within intercept function declare startTime variable and set it to current time that is time when application made the http request.
var startTime = (new Date()).getTime();
- Apply
map
operator tonext.handle(req)
Observable usingpipe
function. Within map operator declare the endTime variable and set it equal to current time i.e. the time on which we received response. - Add startTime and endTime header to the Response.
event = event.clone({ headers: event.headers.set(‘endTime’, endTime.toString()) });
event = event.clone({ headers: event.headers.set(‘startTime’, startTime.toString()) });
- Calculate the difference between startTime and endTime and store it in variable diff. Display the time using console.
- Apply tap operator to next.handle(req) Observable using pipe function. Within tap operator
error =>{}
declare variable endTime and set it to current time i.e. the time on which the response failed. - Calculate the difference between endTime and StartTime and display it in console.
- Note we are using
map
operator to modify response and display total time while we are usingtap
operator to display only total time for error.
a) This is because map operator can be used to tap the response but not error.
b) We can modify the header of response usingmap
operator but not usingtap.
c) We cannot add headers to error becauseerror.headers
is read-only property.
Retry Request Interceptor
Retry Request Interceptor is used to retry the request in case of failure. Sometimes the failure may occur due to poor connection. To encounter this issue we may apply retry operator.
retry-interceptor.service.ts :
- Follow basic steps to implement
HttpInterceptor
. - Within intercept function apply
retry
operator tonext.handle(req)
Observable usingpipe
function.return next.handle(req).pipe(retry(3));
retry(3)
this will retry the request 3 times.
Using all Interceptors
Now we will check how all this Interceptors work. Basically I will show how I invoked different type of API’s to display use of all The Interceptors mentioned above.
- We have added 3 buttons
a) Request Data button will display use ofRequestTimestampService
andAjaxBusyIdentifierInterceptorService
b) Request XML Data button will display use ofRequestTimestampService
,AjaxBusyIdentifierInterceptorService
andXML2JsonInterceptorService
c) Request 404 Data button will display use ofRequestTimestampService
,AjaxBusyIdentifierInterceptorService
,ErrorNotifierService
andRetryInterceptorService
.
Request Data :
When we click on Request data button we send GET request to https://jsonplaceholder.typicode.com/todos/1 URL which responds with JSON data. (JSONPlaceholder is a free online REST API that you can use whenever you need some fake data).
a)RequestTimestampService
will display the total time required in console
b)AjaxBusyIdentifierInterceptorService
will help us display the Ajax Loading Indicator in the header .(in below screenshot you can view the loading animation in top left corner of the screen).
Request XML Data :
When we click on Request data button we send GET request to https://api.openweathermap.org/data/2.5/weather?q=London&mode=xml&appid=myApiID URL which responds with XML data. (using openweatermap we can request weather information which is free api service great for leaning purpose)
a) RequestTimestampService
will display the total time required in console b)AjaxBusyIdentifierInterceptorService
will help us display the Ajax Loading Indicator at the top.
c) XML2JsonInterceptorService
will convert the XML response to JSON response
In above screenshot we can see that the response received was in XML format which was converted to JSON using our interceptor.
Request 404 Data :
When we click on Request 404 Data button we send GET request to https://jsonplaceholder.typicode.com/todos/7878 URL which responds with 404 error data.
a) RequestTimestampService
will display the total time required in console b)AjaxBusyIdentifierInterceptorService
will help us display the Ajax Loading Indicator at the top.
d)ErrorNotifierService
will display the error dialogue box
e) RetryInterceptorService
will retry the request 3 times but the ErrorNotifierService
will display the error message only once because RetryInterceptorService
is added afterwards.
Angular applies interceptors in the order that you provide them. If you provide interceptors A, then B, then C, requests will flow in A->B->C and responses will flow out C->B->A.
- Angular Docs
Github link
You can find the source code of Interceptors project in below github link
https://github.com/SwapnilPakolu/Interceptors
Angular guide link:
https://angular.io/guide/http#intercepting-requests-and-responses
Thanks for reading 😃 ..
Thanks for making it till end. I hope my blog was helpful to you 😄 . Please let me know in comment down below if you have any queries or suggestions. I have tested all the examples demonstrated in this article 😏 still if you find any of the example incorrect 😞 in anyway please let me know I will try to fix it 😁
Please give a clap 👏 for my article 😀😛 if you think it was useful.
Thanks,
Swapnil Pakolu