Http Interceptor in Angular 2

Angular 1 had a concept of http inteceptors for handling things like placing a token on the headers, responding to auth errors and redirecting, responding to all other errors, handling loading spinners, the list goes on. In Angular 2 that is no longer a thing, instead we simply extend the http class.

Extending the Http class

Extending the Http class is straightforward. See my example code below where I create a new Secure Http Service that logs my own messages to the console before calling the base Http request and get methods. While I only log to the console, this is exactly the place you would put any logic that would go in an Http Interceptor:

import { Injectable } from '@angular/core';
import { Http, ConnectionBackend, Headers, RequestOptions, Request, Response, RequestOptionsArgs } from '@angular/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class SecureHttpService extends Http {
  
  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
    super(backend, defaultOptions);
  }

  request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    console.log('My new request...');
    return super.request(url, options);
  }

  get(url: string, options?: RequestOptionsArgs): Observable<Response> {
    console.log('My new get...');
    return super.get(url, options);
  }
}

Interceptor Behavior

This new SecureHttpService can now be used throughout your app (once you declare it as a provider), and I would recommend you go that route. However if you’d like true interceptor behavior, where anytime someone makes a regular http call anywhere in your app your new methods are called, it can be done. Simply provide Http yourself, but have it use your service:

import { NgModule } from '@angular/core';
import { SecureHttpService } from './authenticated-http.service';
import { HttpModule, Http, XHRBackend, RequestOptions } from '@angular/http';

@NgModule({
    imports: [HttpModule],
    declarations: [
    ],
    providers: [{
        provide: Http,
        useFactory: (backend:XHRBackend, defaultOptions:RequestOptions) => {
            return new SecureHttpService(backend, defaultOptions);
        },
        deps: [XHRBackend, RequestOptions]
    }],
    exports: [

    ]
})




And that’s all it takes, when Http is injected it will now use this factory, which returns your SecureHttpService. Just give it a quick try in a component:

import { Page1 } from '../pages/page1/page1';
import { Page2 } from '../pages/page2/page2';

import { Http } from '@angular/http';

@Component({
  templateUrl: 'app.html'
})
export class MyAppComponent {
  @ViewChild('content') nav : Nav;

  root: any = Page1;

  constructor(public platform: Platform, private http: Http) {
    this.initializeApp();

    http.get('https://google.com');
  }
...

Notice I’m just importing Http from ‘@angular/http’ and just injecting regular old Http, but if you check your console you’ll see the messages logged out. Since you declared the Http provider in a module it’s injected at the app root, so all your modules will use your new Http service instead of the original.

It may seem clever, but probably don’t override Http

As clever and interceptor-y as this may seem, it’s probably a bad idea. What if in your authentication module you extend and override Http to handle auth errors and tokens, but then in your UI module you extend and override Http to handle loading spinners and friendly error message. What if an external library overrides Http. Only one of these providers will win, and some of your behavior will be lost.

You’re much better off just declaring SecureHttpService as the provider, and doing so in the highest component you want it shared from. Not overriding Http keeps your code more explicit about what it’s doing and will prevent collisions. You’ll know your “UI” component will need to extend SecureHttpService, and you’ll be confident everything will keep working.




Thoughts? Ideas? Questions? Let me know in the comments below.