Angular - Set headers for every request
Asked Answered
T

23

390

I need to set some Authorization headers after the user has logged in, for every subsequent request.


To set headers for a particular request,

import {Headers} from 'angular2/http';
var headers = new Headers();
headers.append(headerName, value);

// HTTP POST using these headers
this.http.post(url, data, {
  headers: headers
})
// do something with the response

Reference

But it would be not be feasible to manually set request headers for every request in this way.

How do I set the headers set once the user has logged in, and also remove those headers on logout?

Takeo answered 25/12, 2015 at 15:33 Comment(1)
See #34502898Firdausi
T
404

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.

Thorwald answered 25/12, 2015 at 17:48 Comment(10)
where is 'this.http = http;' comes from , I believe we need to declare it before using ?Kelci
angular Headers (set & append functions) is "normalizing" the header's key and makes it lower-case. From Headers.d.ts: // "HTTP character sets are identified by case-insensitive tokens" // Spec at tools.ietf.org/html/rfc2616 For those who doesn't have a backend that works by the spec; here's a bypass: let headersMap = .get(options, 'headers._headersMap', new Map()); headersMap.set('Authorization', [.replace(Bearer ${token}, /\"/g, '')]);Ybarra
@DiegoUnanue I'm using the final version of Angular 2 and Thierry's implementation works. Just replace the 'angular2' to '@angular' in the import statements.Tenenbaum
Mark Pieszak- should I include providers for HttpClient?Ezra
now TS throws error: ` Argument of type '{ headers: Headers; }' is not assignable to parameter of type 'RequestOptionsArgs'`Hatpin
@Hatpin did you import the Headers class from the @angular/http module?Thorwald
@ThierryTemplier Is this method is available for angular 4?Bejewel
The approach is nice but in latest version of angular 'Headers' dose not exists. Below I am posting version working for me in angular9.Lavatory
Hi @ThierryTemplier why you didn't recommend using interceptor more generics and it will be helpful if adding another option to request headers?Decasyllabic
this is wrong - you should be using interceptorsPolynesia
C
210

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';
import { Injectable } from '@angular/core';

@Injectable()
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 {}
Coordinate answered 20/7, 2017 at 17:40 Comment(8)
I implemented this and when doing ng serve i can see the request headers, however when doing ng b prod and deploying inside a tomcat, i don't see the headers... using spring-boot, where did the headers go?Airily
Don't know if it's because I'm working with an Express node API, but it doesn't work for me even with official Angular doc. :/Vyky
ERROR TypeError: CreateListFromArrayLike called on non-objectSteamy
How would you inject anything into HttpInterceptor?Hedvig
I've implemented this same things but in PUT and DELETE API request header doesn't work for me.Ideologist
How to remove header from particular request?Minnaminnaminnie
I prefer the popular answer https://mcmap.net/q/86757/-angular-set-headers-for-every-request. It seems to be simply more efficient because a clone isn't necessary - what if you're POST'ing 10MB?. Also, the popular answer should be easier to debug. But if doing import { HttpClient } from './http-client'; everywhere isn't feasible (perhaps for third-party code), then an interceptor might be a good solution. Perhaps an interceptor to make changes for "black boxes", and use the popular answer above for everything else.Venipuncture
A good security note from the answer below [https://mcmap.net/q/86757/-angular-set-headers-for-every-request] - this interceptor might also send your JWT token (if that's what you're using it for) to third-party web-servers. That would mean they can impersonate you (until the expiry date). To make this answer more secure, you need conditional logic, so the correct headers are only sent for the correct server(s).Venipuncture
C
85

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.

Crosscrosslet answered 27/1, 2016 at 21:5 Comment(11)
What can be done to un-append the headers later ? (For example - user logs out and all authorization has to be cleared)Takeo
this.http._defaultOptions.headers.delete('My-Custom-Header') So the above process can be shortened by following code this.http._defaultOptions.headers.append('My-New-Custom-Header', 'newvalue')Crosscrosslet
See my updated answer for more details on the solutionCrosscrosslet
@anit: _defaultOptions is protected and should not be accessed like thisGermangermana
@Germangermana yes, now I wont recommend myself doing this. I had to come up with this workaround because of angular beta limitations and my habit of controlling the auth flow globally. But I believe for now github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts has a better and clean solution.Crosscrosslet
The problem of using BaseRequestOptions is that it it's constructor is run only once in the application lifetime in browser. So if you want to change header value during time (e.g. csrf_token) you can not do it in this way (even overriding merge method in this class not helps :( )Desma
Problem is that if you use a wrapper 3rd party libraries that access HTTP directly need to be re-written to use it. I still dont know how to get around that. An interceptor is really needed. Not sure if anyone knows a better way.Agenesis
@anit..I am getting this error while using it. @angular/http/index "has no exported member 'HTTP_PROVIDERS'Wirra
@Wirra HTTP_PROVIDERS was deprecated in RC5. In addition, bootstrpa() was also deprecated.Shaniceshanie
Hi, in angular4 _defaultOptions is protected so can't be called from serviceAskja
This does not work when activating routes through ActiveRoute only when you refresh pages. I don't know why.Elaterite
T
25

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]
})
Thorma answered 5/10, 2016 at 5:38 Comment(5)
you need @Injectable and define headers in class, I tested successful by @Injectable() export class CustomRequestOptions extends BaseRequestOptions { headers:Headers = new Headers({ 'Authorization': 'xxx' }); }Peregrinate
well, i did this in 2.0.0, didn't check 2.0.1Peregrinate
Important note here I ran into an issue where it was impossible to inject anything into CustomRequestOptions even when using @Inject/@Injectable. The solution I realized was to extend RequestOptions, not BaseRequestOptions. Providing BaseRequestOptions won't work, but extending RequestOptions instead makes DI work again.Laurilaurianne
This solution is simple, but if user will log out and log back in and his token changes - it will not work anymore, because Authorization header is set only once on application init.Consult
Yes, correct @AlexeyVParamonov. This is useful only if the token are being set once. Otherwise we'll write the interceptors for the case like you said.Thorma
G
16

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.

Guitar answered 1/12, 2016 at 14:20 Comment(5)
This answer worked best for me since I was able to filter the url to my api server and only add the Auth Token to calls made to it. I changed the request to: request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> { var _url: string = url.toString(); if (_url.indexOf('api.myserver.com') > -1) { options = this._setCustomHeaders(options); } return super.request(url, options) }Moll
In my case withCredentials and Headers were taken from url parameter in request method. I changed code like this: request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> { options = this._setCustomHeaders(options); if (typeof(url) === "object") { (<Request>url).withCredentials = options.withCredentials; (<Request>url).headers = options.headers; } return super.request(url, options) }Dkl
The request() method, which you are overloading, has two call signatures and the options property is used only when url specified as string. In case where url is an instance of Request, the options property is just ignored. This could lead to a hard to catch errors. Please see my answer for more details.Malynda
Note that it this solution has some issues with server platform. There are workarounds to avoid it however.Sing
This worked for me until angular 4.2. 4.3 Has interceptors.Rayerayfield
S
15

For Angular 5 and above, we can use HttpInterceptor for generalizing the request and response operations. This helps us avoid duplicating:

1) Common headers

2) Specifying response type

3) Querying request

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpResponse,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';

@Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {

  requestCounter: number = 0;
  constructor() {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    request = request.clone({
      responseType: 'json',
      setHeaders: {
        Authorization: `Bearer token_value`,
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      }
    });

    return next.handle(request).do((event: HttpEvent<any>) => {
      if (event instanceof HttpResponse) {
        // do stuff with response if you want
      }
    }, (err: any) => {
      if (err instanceof HttpErrorResponse) {
        // do stuff with response error if you want
      }
    });
  }
}

We can use this AuthHttpInterceptor class as a provider for the HttpInterceptors:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app.routing-module';
import { AuthHttpInterceptor } from './services/auth-http.interceptor';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    BrowserAnimationsModule,
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthHttpInterceptor,
      multi: true
    }
  ],
  exports: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}
Saturday answered 12/7, 2018 at 9:29 Comment(0)
S
13

Create a custom Http class by extending the Angular 2 Http Provider and simply override the constructor and request method in you custom Http class. The example below adds Authorization header in every http request.

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);
    };
  }
}

Then configure your main app.module.ts to provide the XHRBackend as the ConnectionBackend provider and the RequestOptions to your custom Http class:

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:

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();
    } );
  }
}

Here's a comprehensive guide - http://adonespitogo.com/articles/angular-2-extending-http-provider/

Squalor answered 7/11, 2016 at 3:4 Comment(3)
This approach is well suited for using an alternative class provider. Instead of "provide: HttpService" as you have in your module, you could instead use "provide: Http" allowing you to work with Http as you typically would.Chamomile
How can I add additional properties to this extended http class? For example, router: Router or any custom injectable services.Irreplaceable
@Irreplaceable You can't do that using this. You can define another method in your custom http class, for example setRouter(router). Or you can create another class and inject your custom http class in there instead of the opposite.Squalor
B
8

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 };
Bile answered 15/5, 2017 at 13:13 Comment(1)
little update: for better performance you may consider moving all the static headers (like 'Content-Type') to the constructorLinkoski
P
7

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 { }
Promotion answered 11/10, 2017 at 9:14 Comment(0)
G
6

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.

Gann answered 1/11, 2016 at 15:37 Comment(1)
how did you get tslint to ignore headers as let?Degeneration
K
5

How about Keeping a Separate Service like follows

            import {Injectable} from '@angular/core';
            import {Headers, Http, RequestOptions} from '@angular/http';


            @Injectable()
            export class HttpClientService extends RequestOptions {

                constructor(private requestOptionArgs:RequestOptions) {
                    super();     
                }

                addHeader(headerName: string, headerValue: string ){
                    (this.requestOptionArgs.headers as Headers).set(headerName, headerValue);
                }
            }

and when you calling this from another place use this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

and you will see the added header eg:- Authorization as follows

enter image description here

Kelci answered 7/10, 2016 at 5:3 Comment(0)
Y
5

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.

Youngling answered 5/4, 2017 at 7:15 Comment(5)
Among all the answers given, this is the answer which took my attention since i have already gone for a solution which is based on extending BaseRequestOptions. However, sadly, this did not work for me. any possible reasons?Hercegovina
got it working. this solution is fine and I had a problem in my server. I had to do some configurations for CORS pre-flight requests. refer this link https://mcmap.net/q/87956/-null-http-headers-are-passed-to-the-server-from-angular2-appHercegovina
How do you tie AuthRequestOptions into the rest of the app? I tried putting this in the providers section but it did not do anything.Unroot
You must override the provider for RequestOptions, not BaseRequestOptions. angular.io/api/http/BaseRequestOptionsUnroot
In my app, I just extend BaseRequestOptions, and it already extends RequestOptions. Then in app.module, you should set the providers: { provide: RequestOptions, useClass: AuthRequestOptions }Youngling
G
5

Although I'm answering this very late but if anyone is seeking an easier solution.

We can use angular2-jwt. angular2-jwt is useful automatically attaching a JSON Web Token (JWT) as an Authorization header when making HTTP requests from an Angular 2 app.

We can set global headers with advanced configuration option

export function authHttpServiceFactory(http: Http, options: RequestOptions) {
  return new AuthHttp(new AuthConfig({
    tokenName: 'token',
        tokenGetter: (() => sessionStorage.getItem('token')),
        globalHeaders: [{'Content-Type':'application/json'}],
    }), http, options);
}

And sending per request token like

    getThing() {
  let myHeader = new Headers();
  myHeader.append('Content-Type', 'application/json');

  this.authHttp.get('http://example.com/api/thing', { headers: myHeader })
    .subscribe(
      data => this.thing = data,
      err => console.log(error),
      () => console.log('Request Complete')
    );

  // Pass it after the body in a POST request
  this.authHttp.post('http://example.com/api/thing', 'post body', { headers: myHeader })
    .subscribe(
      data => this.thing = data,
      err => console.log(error),
      () => console.log('Request Complete')
    );
}
Guaiacum answered 14/4, 2017 at 7:37 Comment(1)
would be helpful to goto github.com/auth0/angular2-jwt#installation and adapt this answer using their installation guideDisepalous
M
4

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<Response> {

    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. When Request object is passed instead of the URL string, the options 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.

Malynda answered 14/6, 2017 at 18:50 Comment(0)
A
3

The simplest of all

Create a config.ts file

import { HttpHeaders } from '@angular/common/http';

export class Config {
    url: string = 'http://localhost:3000';
    httpOptions: any = {
        headers: new HttpHeaders({
           'Content-Type': 'application/json',
           'Authorization': JSON.parse(localStorage.getItem('currentUser')).token
        })
    }
}

Then on your service, just import the config.ts file

import { Config } from '../config';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class OrganizationService {
  config = new Config;

  constructor(
    private http: HttpClient
  ) { }

  addData(data): Observable<any> {
     let sendAddLink = `${this.config.url}/api/addData`;

     return this.http.post(sendAddLink , data, this.config.httpOptions).pipe(
       tap(snap => {
      return snap;
        })
    );
 } 

I think it was the simplest and the safest.

Arlenarlena answered 19/3, 2018 at 7:56 Comment(0)
D
2

My biggest issue with setting headers (in 2022) was the fact that append method on HttpHeaders was returning a clone of the original object without modifying the original itself. So headers.append('header', 'value') was not enough. I had to re-assign the result or use it directly in an HTTP call:

let headers = new HttpHeaders();
headers = headers.append('header', 'value');
this.http.get<any>('https://someulr.com/api/users', { headers });
Doit answered 5/4, 2022 at 12:58 Comment(0)
L
1

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);
  }
}
Lavatory answered 12/11, 2020 at 10:45 Comment(0)
D
1

Instead of setting headers again and again for every request , you could use an interceptor.

Every request going out will go through the interceptor where you can set you auth headers and then release the request.

Dingy answered 19/4, 2021 at 19:36 Comment(0)
C
0

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 { }
Caulk answered 6/2, 2017 at 14:33 Comment(1)
Does not work, tried it myself. Doesn't get called on anything but refresh.Elaterite
O
0

I has able to choose a simplier solution > Add a new Headers to the defaults options merge or load by your api get (or other) function.

get(endpoint: string, params?: any, options?: RequestOptions) {
  if (!options) {
    options = new RequestOptions();
    options.headers = new Headers( { "Accept": "application/json" } ); <<<<
  }
  // [...] 
}

Of course you can externalize this Headers in default options or whatever in your class. This is in the Ionic generated api.ts @Injectable() export class API {}

It is very quick and it work for me. I didn't want json/ld format.

Othilie answered 18/7, 2017 at 19:14 Comment(0)
A
0

An HTTP interceptor is the correct way to do this. Not seeing proper docs on how to fully implement it here, so I'm including a link to Google's official guide. I'd read through the docs before implementing as there are a lot of potential pitfalls with security and using multiple interceptor packages.

https://angular.io/guide/http#intercepting-requests-and-responses

import { Injectable } from '@angular/core';
import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';

import { Observable } from 'rxjs';

/** Pass untouched request through to the next request handler. */
@Injectable()
export class NoopInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    return next.handle(req);
  }
}
Altruist answered 2/11, 2021 at 19:42 Comment(0)
F
0
const headers = new HttpHeaders()
  .set('content-type', 'application/json')
  .set('x-functions-key', '');

return this.http.get<Person[]>(baseUrl, {
      headers: headers,
    });

use the append method to append a new value to the existing set of values

headers.append('Access-Control-Allow-Origin', '*')
Finn answered 15/6, 2022 at 20:48 Comment(0)
G
-4

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

Giga answered 25/10, 2016 at 16:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.