I need to set some Authorization headers after the user has logged in, for every subsequent request.
To set headers for a particular request,
You can create your own http client with some authorization header:
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class HttpClientWithAuthorization {
constructor(private http: HttpClient) {}
createAuthorizationHeader(bearerToken: string): HttpHeaders {
const headerDict = {
Authorization: 'Bearer ' + bearerToken,
}
return new HttpHeaders(headerDict);
}
get<T>(url, bearerToken) {
this.createAuthorizationHeader(bearerToken);
return this.http.get<T>(url, {
headers: this.createAuthorizationHeader(bearerToken)
});
}
post<T>(url, bearerToken, data) {
this.createAuthorizationHeader(bearerToken);
return this.http.post<T>(url, data, {
headers: this.createAuthorizationHeader(bearerToken)
});
}
}
And then inject it instead of HttpClient
in your service class:
@Injectable({
providedIn: 'root'
})
export class SomeService {
constructor(readonly httpClientWithAuthorization: HttpClientWithAuthorization) {}
getSomething(): Observable<Object> {
return this.httpClientWithAuthorization.get<Object>(url,'someBearer');
}
postSomething(data) {
return this.httpClientWithAuthorization.post<Object>(url,'someBearer', data);
}
}
There were some changes for angular 2.0.1 and higher:
import {RequestOptions, RequestMethod, Headers} from '@angular/http';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app.routing.module';
import { AppComponent } from './app.component';
//you can move this class to a better place
class GlobalHttpOptions extends RequestOptions {
constructor() {
super({
method: RequestMethod.Get,
headers: new Headers({
'MyHeader': 'MyHeaderValue',
})
});
}
}
@NgModule({
imports: [ BrowserModule, HttpModule, AppRoutingModule ],
declarations: [ AppComponent],
bootstrap: [ AppComponent ],
providers: [ { provide: RequestOptions, useClass: GlobalHttpOptions} ]
})
export class AppModule { }
This is how I did for setting token with every request.
import { RequestOptions, BaseRequestOptions, RequestOptionsArgs } from '@angular/http';
export class CustomRequestOptions extends BaseRequestOptions {
constructor() {
super();
this.headers.set('Content-Type', 'application/json');
}
merge(options?: RequestOptionsArgs): RequestOptions {
const token = localStorage.getItem('token');
const newOptions = super.merge(options);
if (token) {
newOptions.headers.set('Authorization', `Bearer ${token}`);
}
return newOptions;
}
}
And register in app.module.ts
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [
{ provide: RequestOptions, useClass: CustomRequestOptions }
],
bootstrap: [AppComponent]
})
export class AppModule { }
HTTP interceptors are now available via the new HttpClient from @angular/common/http
, as of Angular 4.3.x versions and beyond.
It's pretty simple to add a header for every request now:
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
export class AddHeaderInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Clone the request to add the new header
const clonedRequest = req.clone({ headers: req.headers.append('Authorization', 'Bearer 123') });
// Pass the cloned request instead of the original request to the next handle
return next.handle(clonedRequest);
}
}
There's a principle of immutability, that's the reason the request needs to be cloned before setting something new on it.
As editing headers is a very common task, there's actually a shortcut for it (while cloning the request):
const clonedRequest = req.clone({ setHeaders: { Authorization: 'Bearer 123' } });
After creating the interceptor, you should register it using the HTTP_INTERCEPTORS
provide.
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@NgModule({
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: AddHeaderInterceptor,
multi: true,
}],
})
export class AppModule {}
You can use canActive
in your routes, like so:
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { CanActivate } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate() {
// If user is not logged in we'll send them to the homepage
if (!this.auth.loggedIn()) {
this.router.navigate(['']);
return false;
}
return true;
}
}
const appRoutes: Routes = [
{
path: '', redirectTo: '/deals', pathMatch: 'full'
},
{
path: 'special',
component: PrivateDealsComponent,
/* We'll use the canActivate API and pass in our AuthGuard.
Now any time the /special route is hit, the AuthGuard will run
first to make sure the user is logged in before activating and
loading this route. */
canActivate: [AuthGuard]
}
];
Taken from: https://auth0.com/blog/angular-2-authentication
After some investigation, I found the final and the most easy way is to extend BaseRequestOptions
which I prefer.
The following are the ways I tried and give up for some reason:
1. extend BaseRequestOptions
, and add dynamic headers in constructor()
. It can not work if I login. It will be created once. So it is not dynamic.
2. extend Http
. Same reason as above, I can not add dynamic headers in constructor()
. And if I rewrite request(..)
method, and set headers, like this:
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem(AppConstants.tokenName);
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
options = new RequestOptions({});
}
options.headers.set('Authorization', 'token_value');
} else {
url.headers.set('Authorization', 'token_value');
}
return super.request(url, options).catch(this.catchAuthError(this));
}
You just need to overwrite this method, but not every get/post/put methods.
3.And my preferred solution is extend BaseRequestOptions
and overwrite merge()
:
@Injectable()
export class AuthRequestOptions extends BaseRequestOptions {
merge(options?: RequestOptionsArgs): RequestOptions {
var newOptions = super.merge(options);
let token = localStorage.getItem(AppConstants.tokenName);
newOptions.headers.set(AppConstants.authHeaderName, token);
return newOptions;
}
}
this merge()
function will be called for every request.