How to use Interceptor in Angular

Interceptors are used to intercepts HTTP requests and responses from the application. You can handle them or transform them before passing it on. For example, you can use an interceptor to add authorization token before passing the HTTP request or cache the HTTP response. Some of the usages of interceptors are as follows,

  • Caching of the response
  • Adding request headers such as authorization or any custom header for all outgoing requests.
  • Handling HTTP response error
  • Manipulating the URL
  • Faking the API
  • Authentication for every ongoing request etc.

To implement an interceptor, create a service that implements the HttpInterceptor interface.  The HttpInterceptor interface has one method intercept, which returns observable of HttpEvent and takes two inputs of types HttpRequest and HttpHandler.

abc

You can create an interceptor service as shown below,



import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable()
export class GlobalInterceptor implements HttpInterceptor{

    intercept(request :HttpRequest<any>,next:HttpHandler):Observable<HttpEvent<any>>{

        console.log(request.url);
        console.log(request.headers);
        console.log(request.body);
        return next.handle(request);
       // return null; 
    }
}


The above interceptor is simply printing request URL, headers, and body before passing the request object to the next method such that it can send it to the server.

After creating the interceptor service, you need to provide it to the application root module level.



@NgModule({
  declarations: [
    AppComponent,
    ProductsComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [{
    provide:ErrorHandler,useClass:GlobalErrorHandler},
   {provide:HTTP_INTERCEPTORS, useClass: GlobalInterceptor,multi:true}],
  bootstrap: [AppComponent]
})


 

Some important usage scenarios keep in mind that,

  • Import HttpClientModule only in the AppModule means root module
  • Add interceptors in the provide array of the AppModule
  • In case, if you import HttpClientModule in features module, then each module creates a copy of HttpClientModule, which overwrites the interceptor provided the AppModule.
  • You can provide more than one interceptor in the application. However, Angular apply them in the order they are provided.

Adding Headers

There are many usages of an interceptor, for example, if you want to set the header for every HTTP request, amend the request body, or set authorization header.  To set the header, first, clone the request object and set the header property.  You can also set the Content-Type and Accept header as shown below,



  // setting the header before sending the request
       const r = request.clone(
            {
            url:request.url.replace('http://','https://'),
            headers: request.headers.set('Content-Type', 'application/json')
            .set('Accept', 'application/json')
           }
        );

        return next.handle(r);


The above interceptor also replaces all request URLs from HTTP to HTTPS.  You can also set any new header means which are not standard HTTP header using the setHeader method,



 const r = request.clone(
            {
           // url:request.url.replace('http://','https://'),
            headers: request.headers.set('Content-Type', 'application/json')
            .set('Accept', 'application/json'),
            setHeaders:{'geek97':'ngIndia'}

           }
        );

        return next.handle(r);

 


Caching

The best way, you can use Interceptors request caching.  To cache requests, create an Angular service in which you register outgoing HTTP requests for caching.



import { Injectable } from "@angular/core";
import { HttpRequest, HttpResponse } from '@angular/common/http';

const maxAge = 20000;
@Injectable()
export class CacheRegistrationService {

    cache = new Map();
    get(req: HttpRequest<any>): HttpResponse<any> | undefined {
        const url = req.urlWithParams;
        const cached = this.cache.get(url);

        if (!cached) {
          return undefined;
        }

        const isExpired = cached.lastRead < (Date.now() - maxAge);
        const expired = isExpired ? 'expired ' : '';
        return cached.response;
      }

      put(req: HttpRequest<any>, response: HttpResponse<any>): void {
        const url = req.url;
        const entry = { url, response, lastRead: Date.now() };
        this.cache.set(url, entry);

        const expired = Date.now() - maxAge;
        this.cache.forEach(expiredEntry => {
          if (expiredEntry.lastRead < expired) {
            this.cache.delete(expiredEntry.url);
          }
        });
      }
}

 


 

You can have your own logic to register a request for the caching. However, I am just using a JavaScript Map() object to save outgoing requests for the caching.

I have written another post detailing about Maps in JavaScript here

Next, provide CacheRegisterationService at the AppModule level.



providers: [CacheRegistrationService, {
    provide: ErrorHandler, useClass: GlobalErrorHandler
  },
    { provide: HTTP_INTERCEPTORS, useClass: GlobalInterceptor, multi: true }],
  bootstrap: [AppComponent]


Now you can modify interceptor for caching as shown below,


@Injectable()
export class GlobalInterceptor implements HttpInterceptor {

  constructor(private cacheRegistrationService: CacheRegistrationService) {
    }
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

       const cachedResponse = this.cacheRegistrationService.get(request);
       return cachedResponse ? of(cachedResponse) : this.sendRequest(request, next, this.cacheRegistrationService);

      // return this.sendRequest(request,next,this.cacheRegistrationService);

    }
    sendRequest(
        request: HttpRequest<any>,
        next: HttpHandler,
        cache: CacheRegistrationService): Observable<HttpEvent<any>> {

    // cloning the request to modify the hedaer 
       const r = request.clone(
            {
                // url:request.url.replace('http://','https://'),
                headers: request.headers.set('Content-Type', 'application/json')
                    .set('Accept', 'application/json'),
                setHeaders: { 'geek97': 'ngIndia' }

            }
        );

        // Making the request and caching 

        return next.handle(r).pipe(
            tap(event => {
                if (event instanceof HttpResponse) {
                    cache.put(r, event);
                }
            })
        );
    }
}


We are just checking whether requests are cached or not before making request. In the network tab, you can check that only one request is made for multiple GET operations on the same API URL.

Handling HTTP error

You can also use interceptors for handling HTTP communication errors.  You can capture HTTP response error 401 and before that try request two times as shown below,


return next.handle(r).pipe(

            retry(2),
            catchError((error :HttpErrorResponse)=>{
                if(error.status ){
                    // log the eroor. pass it to global error handler
                    alert('error 401')
                }
                return throwError(error);
            })
        );


 

These are the few scenarios in which you can use the interceptor. I hope you like this post. For online or onsite Angular training reach me at debugmode@outlook.com .

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s