Using HTTP Interceptors in Angular 2

What are HTTP Interceptors?

In Angular 1.x there was an option to register objects as HTTP Interceptors. You could then use those interceptors to perform different operations and transformations on all HTTP calls made by your application. This was a very powerful feature of the framework because not only allowed setting things like base path for the REST API endpoint, CSRF headers and many other things but also transforming or caching responses from the server.

So what’s different in Angular 2?

Well, after looking at the documentation you won’t be able to find any reference to interceptors in the new version of the framework. At least for now, interceptors are not currently supported out of the box. You could just use the classic way of doing things and just extend the HTTP class in Angular 2 but that’s not very pretty.

Introducing the XHRBackend

Angular 2 uses this class to create all HTTP connections. By taking advantage of the Angular’s great DI engine you could potentially extend the base XHRBackend class and provide our custom implementation to the application. By taking control of the creation of the HTTP connections you will be able to implement the classic Angular interceptors this way.

Let’s look at some code

Let’s start by creating our own XHRBackend.

MyXHRBackend.ts

import {XHRBackend, Request, XHRConnection, Response} from '@angular/http';
import {Observable} from 'rxjs';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

export class MyXHRBackend extends XHRBackend {

  createConnection(request: Request): XHRConnection {
    let connection: XHRConnection = super.createConnection(request);
    // Before returning the connection we try to catch all possible errors(4XX,5XX and so on)
    connection.response = connection.response.catch(this.processResponse);
    return connection;
  }

  processResponse(response:Response){
    switch (response.status) {
      case 401:
        // You could redirect to login page here
        return Observable.throw('your custom error here');
      case 403:
        // You could redirect to forbidden page here
        return Observable.throw('your custom error here');
      case 404:
        // You could redirect to 404 page here
        return Observable.throw('your custom error here');
      default:
        return Observable.throw(response);
    }
  }

}

The above XHRBackend extension will catch all 401, 403 and 404 errors.
In order to tell Angular to use our implementation instead of the default class we will use Angular’s great DI features. We just need to add our custom class to the providers list in the application main module.

AppModule.ts

import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {HttpModule, XHRBackend} from '@angular/http';
import {AppComponent} from './app.component';
import {MyXHRBackend} from './MyXHRBackend';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    HttpModule
  ],
  providers: [
    {provide: XHRBackend, useClass: MyXHRBackend}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

By providing our custom implementation of the XHRBackend, Angular’s HTTP class will use this implementation for all http calls. This means that if any http call encounters 401, 403 or 404 errors we can act accordingly in our application.

13 comments on “Using HTTP Interceptors in Angular 2

  1. Yaron moarad says:

    Hi, nice article.
    But you can achieve the same thing by extending the Http class so why not do that?
    It’s almost the same implementation and for sure the same way to provide it in your module.

    Like

  2. Daniel Popescu says:

    I don’t see any reason why you shouldn’t do that. There is also an option to extend the BaseRequestOptions class. I think that, in the end, is a matter of preference and what level of control you want when you extend parts of the framework. In my code I always use this method

    Like

  3. Yaron moarad says:

    The XHRBackend is defined as experimental. I hope it won’t break.

    Like

  4. Daniel Popescu says:

    The Http class is also marked as experimental for some reason. – https://angular.io/docs/ts/latest/api/http/index/Http-class.html

    Like

  5. Danillo Babrosa says:

    I implemented this and it worked perfectly for me.
    I added a static EventEmitter to the MyXHRBackend class so that my authentication service can listen for errors and use the router to guide the user to error and login routes without needing to use window.location.

    Liked by 1 person

  6. Dusty Countryman says:

    So quick question, is it possible to look at the URL that was called and return a different custom response based on URL? I.E. If they are trying to loging (http://xxxxx/api/auth/login then I want to return 401, but if is another URL and a 401 I want to redirect them to the login page, because their JWT token may have expired?

    Like

    • Dusty Countryman says:

      Actually figured it out! Thanks.

      case 401:
      console.log(response)
      if (response.url.includes(“api/auth/l3gin”)) {
      return Observable.throw(‘401’);
      } else {
      // You could redirect to login page here
      return Observable.throw(‘Going to Redirect’);
      }

      Like

  7. Constantin says:

    Bravo Daniel. Good article. I had the option to use your code or the ng-http-interceptor. I ended up using your solution. It’s just easier and doesn’t require 100 new items to install and update.

    Liked by 1 person

  8. Alex Betz says:

    I am an angular 2 newbie. I have a user.service.ts whichI would like to inject in order to reset my user on a 401 error. This in itself will redirect the user to the login page. I have problems injecting the service into MyXHRBackend.ts. How would you redirect to the login page? (case 401:
    // You could redirect to login page here; return Observable.throw(‘your custom error here’); ?

    Like

    • even says:

      you can define a static Subject on MyXHRBackend class,and in any component inject Router provider,import this static Subject,then subscribe it,finally in the place(// You could redirect to login page here; ) emit it.

      in your MyXHRBackend class:
      export class MyXHRBackend extends XHRBackend {
      ……
      static emitter: Subject = new Subject();
      ……

      case 401:
      // You could redirect to login page here
      MyXHRBackend.emitter.next(‘any data’);
      return Observable.throw(‘your custom error here’);

      }

      in your component:
      class yourComponent {

      constructor( private router: Router) {

      }

      ngOnInit() {
      let subscription= MyXHRBackend.emitter.subscribe((d) => {
      //you can use router sercive
      }
      }

      }

      Like

Leave a comment