I need to set some Authorization headers after the user has logged in, for every subsequent request.
To set headers for a particular request,
Better late than never... =)
You may take the concept of extended BaseRequestOptions
(from here https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options) and refresh the headers "on the fly" (not only in constructor). You may use getter/setter "headers" property override like this:
import { Injectable } from '@angular/core';
import { BaseRequestOptions, RequestOptions, Headers } from '@angular/http';
@Injectable()
export class DefaultRequestOptions extends BaseRequestOptions {
private superHeaders: Headers;
get headers() {
// Set the default 'Content-Type' header
this.superHeaders.set('Content-Type', 'application/json');
const token = localStorage.getItem('authToken');
if(token) {
this.superHeaders.set('Authorization', `Bearer ${token}`);
} else {
this.superHeaders.delete('Authorization');
}
return this.superHeaders;
}
set headers(headers: Headers) {
this.superHeaders = headers;
}
constructor() {
super();
}
}
export const requestOptionsProvider = { provide: RequestOptions, useClass: DefaultRequestOptions };
In Angular 2.1.2
I approached this by extending the angular Http:
import {Injectable} from "@angular/core";
import {Http, Headers, RequestOptionsArgs, Request, Response, ConnectionBackend, RequestOptions} from "@angular/http";
import {Observable} from 'rxjs/Observable';
@Injectable()
export class HttpClient extends Http {
constructor(protected _backend: ConnectionBackend, protected _defaultOptions: RequestOptions) {
super(_backend, _defaultOptions);
}
_setCustomHeaders(options?: RequestOptionsArgs):RequestOptionsArgs{
if(!options) {
options = new RequestOptions({});
}
if(localStorage.getItem("id_token")) {
if (!options.headers) {
options.headers = new Headers();
}
options.headers.set("Authorization", localStorage.getItem("id_token"))
}
return options;
}
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
options = this._setCustomHeaders(options);
return super.request(url, options)
}
}
then in my App Providers I was able to use a custom Factory to provide 'Http'
import { RequestOptions, Http, XHRBackend} from '@angular/http';
import {HttpClient} from './httpClient';
import { RequestOptions, Http, XHRBackend} from '@angular/http';
import {HttpClient} from './httpClient';//above snippet
function httpClientFactory(xhrBackend: XHRBackend, requestOptions: RequestOptions): Http {
return new HttpClient(xhrBackend, requestOptions);
}
@NgModule({
imports:[
FormsModule,
BrowserModule,
],
declarations: APP_DECLARATIONS,
bootstrap:[AppComponent],
providers:[
{ provide: Http, useFactory: httpClientFactory, deps: [XHRBackend, RequestOptions]}
],
})
export class AppModule {
constructor(){
}
}
now I don't need to declare every Http method and can use http
as normal throughout my application.
To answer, you question you could provide a service that wraps the original Http
object from Angular. Something like described below.
import {Injectable} from '@angular/core';
import {Http, Headers} from '@angular/http';
@Injectable()
export class HttpClient {
constructor(private http: Http) {}
createAuthorizationHeader(headers: Headers) {
headers.append('Authorization', 'Basic ' +
btoa('username:password'));
}
get(url) {
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this.http.get(url, {
headers: headers
});
}
post(url, data) {
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this.http.post(url, data, {
headers: headers
});
}
}
And instead of injecting the Http
object you could inject this one (HttpClient
).
import { HttpClient } from './http-client';
export class MyComponent {
// Notice we inject "our" HttpClient here, naming it Http so it's easier
constructor(http: HttpClient) {
this.http = httpClient;
}
handleSomething() {
this.http.post(url, data).subscribe(result => {
// console.log( result );
});
}
}
I also think that something could be done using multi providers for the Http
class by providing your own class extending the Http
one... See this link: http://blog.thoughtram.io/angular2/2015/11/23/multi-providers-in-angular-2.html.
Extending BaseRequestOptions
might be of great help in this scenario. Check out the following code:
import {provide} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {HTTP_PROVIDERS, Headers, Http, BaseRequestOptions} from 'angular2/http';
import {AppCmp} from './components/app/app';
class MyRequestOptions extends BaseRequestOptions {
constructor () {
super();
this.headers.append('My-Custom-Header','MyCustomHeaderValue');
}
}
bootstrap(AppCmp, [
ROUTER_PROVIDERS,
HTTP_PROVIDERS,
provide(RequestOptions, { useClass: MyRequestOptions })
]);
This should include 'My-Custom-Header' in every call.
Update:
To be able to change the header anytime you want instead of above code you can also use following code to add a new header:
this.http._defaultOptions.headers.append('Authorization', 'token');
to delete you can do
this.http._defaultOptions.headers.delete('Authorization');
Also there is another function that you can use to set the value:
this.http._defaultOptions.headers.set('Authorization', 'token');
Above solution still is not completely valid in typescript context. _defaultHeaders is protected and not supposed to be used like this. I would recommend the above solution for a quick fix but for long run its better to write your own wrapper around http calls which also handles auth. Take following example from auth0 which is better and clean.
https://github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts
Update - June 2018 I see a lot of people going for this solution but I would advise otherwise. Appending header globally will send auth token to every api call going out from your app. So the api calls going to third party plugins like intercom or zendesk or any other api will also carry your authorization header. This might result into a big security flaw. So instead, use interceptor globally but check manually if the outgoing call is towards your server's api endpoint or not and then attach auth header.
Although I'm answering it very late but it might help someone else. To inject headers to all requests when @NgModule
is used, one can do the following:
(I tested this in Angular 2.0.1)
/**
* Extending BaseRequestOptions to inject common headers to all requests.
*/
class CustomRequestOptions extends BaseRequestOptions {
constructor() {
super();
this.headers.append('Authorization', 'my-token');
this.headers.append('foo', 'bar');
}
}
Now in @NgModule
do the following:
@NgModule({
declarations: [FooComponent],
imports : [
// Angular modules
BrowserModule,
HttpModule, // This is required
/* other modules */
],
providers : [
{provide: LocationStrategy, useClass: HashLocationStrategy},
// This is the main part. We are telling Angular to provide an instance of
// CustomRequestOptions whenever someone injects RequestOptions
{provide: RequestOptions, useClass: CustomRequestOptions}
],
bootstrap : [AppComponent]
})
Here is an improved version of the accepted answer, updated for Angular2 final :
import {Injectable} from "@angular/core";
import {Http, Headers, Response, Request, BaseRequestOptions, RequestMethod} from "@angular/http";
import {I18nService} from "../lang-picker/i18n.service";
import {Observable} from "rxjs";
@Injectable()
export class HttpClient {
constructor(private http: Http, private i18n: I18nService ) {}
get(url:string):Observable<Response> {
return this.request(url, RequestMethod.Get);
}
post(url:string, body:any) {
return this.request(url, RequestMethod.Post, body);
}
private request(url:string, method:RequestMethod, body?:any):Observable<Response>{
let headers = new Headers();
this.createAcceptLanguageHeader(headers);
let options = new BaseRequestOptions();
options.headers = headers;
options.url = url;
options.method = method;
options.body = body;
options.withCredentials = true;
let request = new Request(options);
return this.http.request(request);
}
// set the accept-language header using the value from i18n service that holds the language currently selected by the user
private createAcceptLanguageHeader(headers:Headers) {
headers.append('Accept-Language', this.i18n.getCurrentLang());
}
}
Of course it should be extended for methods like delete
and put
if needed (I don't need them yet at this point in my project).
The advantage is that there is less duplicated code in the get
/post
/... methods.
Note that in my case I use cookies for authentication. I needed the header for i18n (the Accept-Language
header) because many values returned by our API are translated in the user's language. In my app the i18n service holds the language currently selected by the user.