Been learning Angular 2 for a week now. Though most of the Angular 1.x concepts are familiar in Angular 2, they are now implemented differently. One hendous problem I encountered was creating a token-based authentication system for my personal project. The api is an Express application that uses express-jwt plugin. Thus, I had to add authorization
header to every http requests containing the authentication token.
In Angular 1.x, we use $http interceptors
to hijack every http requests. But Angular 2 doensn’t provide the same functionality. The best solution I can think of is to extend the Http
class and make a custom Http provider which automatically adds the authentication token to every http request.
The most frustrating problem I had during my attempt to extend the Http
class was encountering an error No provider for ConnectionBackend!
. Most of the solutions provided in Stack Overflow are for the pre-release versions of Angular 2, which have changed after the final release. I’m on Angular version 2.1.1
at the time of writing this article. After some reading, I found out that Angular 2 has multiple providers for ConnectionBackend
namely: JSONPBackend_
and XHRBackend
. Let’s use the XHRBackend
from @angular/http
module.
Create Custom Http Class
First, lets create our custom http provider class.
http.service.ts
import {Injectable} from '@angular/core';
import {Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
@Injectable()
export class HttpService extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = localStorage.getItem('auth_token'); // your custom token getter function here
options.headers.set('Authorization', `Bearer ${token}`);
super(backend, options);
}
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('auth_token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = {headers: new Headers()};
}
options.headers.set('Authorization', `Bearer ${token}`);
} else {
// we have to add the token to the url object
url.headers.set('Authorization', `Bearer ${token}`);
}
return super.request(url, options).catch(this.catchAuthError(this));
}
private catchAuthError (self: HttpService) {
// we have to pass HttpService's own instance here as `self`
return (res: Response) => {
console.log(res);
if (res.status === 401 || res.status === 403) {
// if not authenticated
console.log(res);
}
return Observable.throw(res);
};
}
}
Configure the Custom Http Class
Now, we need to configure our main module to provide the XHRBackend
to our custom http class. In your main module declaration, add the following to the providers array:
app.module.ts
import { HttpModule, RequestOptions, XHRBackend } from '@angular/http';
import { HttpService } from './services/http.service';
...
@NgModule({
imports: [..],
providers: [
{
provide: HttpService,
useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new HttpService(backend, options);
},
deps: [XHRBackend, RequestOptions]
}
],
bootstrap: [ AppComponent ]
})
After that, you can now use your custom http provider in your services. For example:
user.service.ts
import { Injectable } from '@angular/core';
import {HttpService} from './http.service';
@Injectable()
class UserService {
constructor (private http: HttpService) {}
// token will added automatically to get request header
getUser (id: number) {
return this.http.get(`/users/${id}`).map((res) => {
return res.json();
} );
}
}
That’s all there is to create your custom http provider. Thanks for reading.