I need to set some Authorization headers after the user has logged in, for every subsequent request.
To set headers for a particular request,
I like the idea to override default options, this seems like a good solution.
However, if you are up to extending the Http
class. Make sure to read this through!
Some answers here are actually showing incorrect overloading of request()
method, which could lead to a hard-to-catch errors and weird behavior. I've stumbled upon this myself.
This solution is based on request()
method implementation in Angular 4.2.x
, but should be future-compatible:
import {Observable} from 'rxjs/Observable';
import {Injectable} from '@angular/core';
import {
ConnectionBackend, Headers,
Http as NgHttp,
Request,
RequestOptions,
RequestOptionsArgs,
Response,
XHRBackend
} from '@angular/http';
import {AuthenticationStateService} from '../authentication/authentication-state.service';
@Injectable()
export class Http extends NgHttp {
constructor (
backend: ConnectionBackend,
defaultOptions: RequestOptions,
private authenticationStateService: AuthenticationStateService
) {
super(backend, defaultOptions);
}
request (url: string | Request, options?: RequestOptionsArgs): Observable {
if ('string' === typeof url) {
url = this.rewriteUrl(url);
options = (options || new RequestOptions());
options.headers = this.updateHeaders(options.headers);
return super.request(url, options);
} else if (url instanceof Request) {
const request = url;
request.url = this.rewriteUrl(request.url);
request.headers = this.updateHeaders(request.headers);
return super.request(request);
} else {
throw new Error('First argument must be a url string or Request instance');
}
}
private rewriteUrl (url: string) {
return environment.backendBaseUrl + url;
}
private updateHeaders (headers?: Headers) {
headers = headers || new Headers();
// Authenticating the request.
if (this.authenticationStateService.isAuthenticated() && !headers.has('Authorization')) {
headers.append('Authorization', 'Bearer ' + this.authenticationStateService.getToken());
}
return headers;
}
}
Notice that I'm importing original class this way import { Http as NgHttp } from '@angular/http';
in order to prevent name clashes.
The problem addressed here is that
request()
method has two different call signatures. WhenRequest
object is passed instead of the URLstring
, theoptions
argument is ignored by Angular. So both cases must be properly handled.
And here's the example of how to register this overridden class with DI container:
export const httpProvider = {
provide: NgHttp,
useFactory: httpFactory,
deps: [XHRBackend, RequestOptions, AuthenticationStateService]
};
export function httpFactory (
xhrBackend: XHRBackend,
requestOptions: RequestOptions,
authenticationStateService: AuthenticationStateService
): Http {
return new Http(
xhrBackend,
requestOptions,
authenticationStateService
);
}
With such approach you can inject Http
class normally, but your overridden class will be magically injected instead. This allows you to integrate your solution easily without changing other parts of the application (polymorphism in action).
Just add httpProvider
to the providers
property of your module metadata.