Angolare: imposta le intestazioni per ogni richiesta


334

Devo impostare alcune intestazioni di autorizzazione dopo che l'utente ha effettuato l'accesso, per ogni richiesta successiva.


Per impostare le intestazioni per una particolare richiesta,

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

Riferimento

Ma non sarebbe possibile impostare manualmente le intestazioni delle richieste per ogni richiesta in questo modo.

Come posso impostare le intestazioni impostate una volta che l'utente ha effettuato l'accesso e rimuovere anche quelle intestazioni al logout?



Risposte:


379

Per rispondere, ti chiedi che potresti fornire un servizio che avvolge l' Httpoggetto originale da Angular. Qualcosa come descritto di seguito.

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

E invece di iniettare l' Httpoggetto potresti iniettare questo ( 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 );
    });
  }
}

Penso anche che si possa fare qualcosa usando multi provider per la Httpclasse fornendo la propria classe estendendo Httpquella ... Vedi questo link: http://blog.thoughtram.io/angular2/2015/11/23/multi-providers -in-angular-2.html .


1
dove è 'this.http = http;' proviene da, credo che dobbiamo dichiararlo prima dell'uso?
co2f2e,

1
Le intestazioni angolari (funzioni di impostazione e aggiunta) "normalizzano" la chiave dell'intestazione e la rendono minuscola. Da Headers.d.ts: // "I set di caratteri HTTP sono identificati da token senza distinzione tra maiuscole e minuscole" // Spec at tools.ietf.org/html/rfc2616 Per coloro che non hanno un backend che funziona secondo le specifiche; ecco un bypass: let headersMap = .get (opzioni, 'headers._headersMap', nuova mappa ()); headersMap.set ('Authorization', [ .replace ( Bearer ${token}, / \ "/ g, '')]);
sangress

@DiegoUnanue Sto usando la versione finale di Angular 2 e le opere di implementazione di Thierry. Sostituisci semplicemente "angular2" in "@angular" nelle istruzioni di importazione.
f123,

Mark Pieszak- dovrei includere i fornitori di HttpClient?
user3127109

ora TS genera l'errore: `Argomento di tipo '{headers: Headers; } 'non è assegnabile al parametro di tipo' RequestOptionsArgs'`
elporfirio

142

Intercettori HTTP sono ora disponibili tramite il nuovo HttpClientda @angular/common/http, come di versioni 4.3.x angolari e di là .

È abbastanza semplice aggiungere un'intestazione per ogni richiesta ora:

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.set('Authorization', 'Bearer 123') });

    // Pass the cloned request instead of the original request to the next handle
    return next.handle(clonedRequest);
  }
}

C'è un principio di immutabilità , questa è la ragione per cui la richiesta deve essere clonata prima di impostare qualcosa di nuovo su di essa.

Poiché la modifica delle intestazioni è un'attività molto comune, in realtà esiste un collegamento (durante la clonazione della richiesta):

const clonedRequest = req.clone({ setHeaders: { Authorization: 'Bearer 123' } });

Dopo aver creato l'interceptor, è necessario registrarlo utilizzando il HTTP_INTERCEPTORScomando fornire.

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}

Ho implementato questo e quando eseguo il servizio posso vedere le intestazioni della richiesta, tuttavia quando eseguo un prodotto e lo schieramento all'interno di un tomcat, non vedo le intestazioni ... usando Spring-Boot, dove sono finite le intestazioni?
naoru,

1
Non so se è perché sto lavorando con un'API del nodo Express, ma non funziona anche con il documento angolare ufficiale. : /
Maxime Lafarie il

ERRORE TypeError: CreateListFromArrayLike chiamato su non oggetto
DAG

1
Come inietteresti qualcosa in HttpInterceptor?
zaitsman,

Ho implementato le stesse cose ma nell'intestazione della richiesta API PUT e DELETE non funziona per me.
Pooja,

78

L'estensione BaseRequestOptionspotrebbe essere di grande aiuto in questo scenario. Dai un'occhiata al seguente codice:

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

Ciò dovrebbe includere "My-Custom-Header" in ogni chiamata.

Aggiornare:

Per poter cambiare l'intestazione ogni volta che vuoi invece del codice sopra puoi anche usare il seguente codice per aggiungere una nuova intestazione:

this.http._defaultOptions.headers.append('Authorization', 'token');

per eliminare puoi fare

this.http._defaultOptions.headers.delete('Authorization');

Inoltre c'è un'altra funzione che puoi usare per impostare il valore:

this.http._defaultOptions.headers.set('Authorization', 'token');

La soluzione precedente non è ancora completamente valida nel contesto dattiloscritto. _defaultHeaders è protetto e non dovrebbe essere usato in questo modo. Vorrei raccomandare la soluzione di cui sopra per una soluzione rapida, ma a lungo termine è meglio scrivere il proprio wrapper intorno alle chiamate http che gestisce anche auth. Prendi il seguente esempio da auth0 che è migliore e pulito.

https://github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts

Aggiornamento - giugno 2018 vedo molte persone che cercano questa soluzione, ma consiglierei diversamente. L'aggiunta dell'intestazione a livello globale invierà il token di autenticazione a ogni chiamata API che esce dalla tua app. Quindi le chiamate API che vanno a plugin di terze parti come interfono o zendesk o qualsiasi altra API porteranno anche la tua intestazione di autorizzazione. Ciò potrebbe comportare un grande difetto di sicurezza. Quindi, invece, usa l'interceptor a livello globale ma controlla manualmente se la chiamata in uscita è verso l'endpoint api del tuo server o meno e quindi collega l'intestazione di autenticazione.


1
this.http._defaultOptions.headers.delete ('My-Custom-Header') Quindi il processo sopra può essere abbreviato seguendo il codice this.http._defaultOptions.headers.append ('My-New-Custom-Header', 'newvalue ')
anit

2
@Dinistro sì, ora non mi raccomando di farlo. Ho dovuto escogitare questa soluzione alternativa a causa delle limitazioni angolari della beta e della mia abitudine di controllare il flusso di autorizzazione a livello globale. Ma per ora credo che github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts abbia una soluzione migliore e pulita.
anit

1
Il problema dell'utilizzo di BaseRequestOptions è che il suo costruttore viene eseguito solo una volta nella vita dell'applicazione nel browser. Quindi, se si desidera modificare il valore dell'intestazione nel tempo (ad es. Csrf_token), non è possibile farlo in questo modo (anche ignorare il metodo di unione in questa classe non aiuta :()
Kamil Kiełczewski

1
Il problema è che se si utilizza un wrapper librerie di terze parti che accedono direttamente a HTTP devono essere riscritte per utilizzarlo. Non so ancora come evitarlo. È davvero necessario un intercettore. Non sono sicuro se qualcuno conosce un modo migliore.
Piotr Stulinski,

6
Ciao, in angular4 _defaultOptionsè protetto, quindi non può essere chiamato dal servizio
Andurit

24

Anche se sto rispondendo molto tardi, ma potrebbe aiutare qualcun altro. Per iniettare le intestazioni a tutte le richieste quando @NgModuleviene utilizzato, si può fare quanto segue:

(Ho provato questo 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');
    }
}

Ora @NgModulefai quanto segue:

@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]
})

4
hai bisogno di @Injectable e definisci le intestazioni in classe, ho testato con successo con la classe di esportazione @Injectable () CustomRequestOptions estende BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack,

bene, l'ho fatto in 2.0.0, non ho controllato 2.0.1
EasonBlack il

Nota importante qui ho riscontrato un problema in cui era impossibile iniettare qualcosa CustomRequestOptionsanche quando si utilizza @ Inject / @ Injectable. La soluzione che ho realizzato è stata quella di estendere RequestOptions, non BaseRequestOptions. Fornire BaseRequestOptionsnon funzionerà, ma l'estensione RequestOptionsinvece farà nuovamente funzionare DI.
parlamento

5
Questa soluzione è semplice, ma se l'utente si disconnetterà e riconnetterà e il suo token cambierà, non funzionerà più, poiché l' Authorizationintestazione viene impostata una sola volta sull'inizial dell'applicazione.
Alex Paramonov,

Sì, corretto @AlexeyVParamonov. Ciò è utile solo se il token viene impostato una volta. Altrimenti scriveremo gli intercettori per il caso come hai detto tu.
Shashank Agrawal,

15

In Angular 2.1.2mi sono avvicinato a questo estendendo l'Http angolare:

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

quindi nei miei provider di app sono stato in grado di utilizzare una Factory personalizzata per fornire "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(){

  }
}

ora non ho bisogno di dichiarare ogni metodo Http e posso usarlo httpnormalmente in tutta la mia applicazione.


Questa risposta ha funzionato meglio per me poiché sono stato in grado di filtrare l'URL sul mio server API e aggiungere solo il token di autenticazione alle chiamate effettuate. Ho modificato la richiesta in: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {options = this._setCustomHeaders (opzioni); } return super.request (url, opzioni)}
Chris Holcomb il

Nel mio caso withCredentials and Headers sono stati presi dal parametro url nel metodo di richiesta. Ho modificato il codice in questo modo: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {options = this._setCustomHeaders (opzioni); if (typeof (url) === "oggetto") {(<Request> url) .withCredentials = options.withCredentials; (<Request> url) .headers = options.headers; } return super.request (url, opzioni)}
Argnist

Il request()metodo, che si sta sovraccaricando, ha due firme di chiamata e la optionsproprietà viene utilizzata solo se urlspecificata come stringa. Nel caso in cui urlsia presente un'istanza di Request, la optionsproprietà viene semplicemente ignorata. Ciò potrebbe causare errori difficili da individuare. Si prega di consultare la mia risposta per maggiori dettagli.
Slava Fomin II

Si noti che questa soluzione presenta alcuni problemi con la piattaforma server . Ci sono soluzioni alternative per evitarlo comunque.
Alireza Mirian,

Questo ha funzionato per me fino all'angolazione 4.2. 4.3 Ha intercettori.
cabaji99,

12

Crea una classe Http personalizzata estendendo il Httpprovider Angular 2 e semplicemente sostituendo il metodo constructore requestnella tua classe Http personalizzata. L'esempio seguente aggiunge l' Authorizationintestazione in ogni richiesta http.

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

Quindi configura il tuo main app.module.tsper fornire XHRBackendcome ConnectionBackendprovider e RequestOptionsper la tua classe Http personalizzata:

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

Successivamente, ora puoi utilizzare il tuo provider http personalizzato nei tuoi servizi. Per esempio:

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

Ecco una guida completa - http://adonespitogo.com/articles/angular-2-extending-http-provider/


Questo approccio è adatto per l'utilizzo di un provider di classi alternativo. Invece di "fornire: HttpService" come hai nel tuo modulo, puoi invece usare "fornire: Http" che ti consente di lavorare con Http come faresti normalmente.
The Gilbert Arenas Dagger,

Come posso aggiungere proprietà aggiuntive a questa classe http estesa? Ad esempio, router: router o qualsiasi servizio iniettabile personalizzato.
shafeequemat,

@shafeequemat Non puoi farlo usando questo. Ad esempio, puoi definire un altro metodo nella tua classe http personalizzata setRouter(router). Oppure puoi creare un'altra classe e iniettare la tua classe http personalizzata invece che l'opposto.
Adones Pitogo,

9

Per Angular 5 e versioni successive, possiamo utilizzare HttpInterceptor per generalizzare le operazioni di richiesta e risposta. Questo ci aiuta a evitare la duplicazione:

1) Intestazioni comuni

2) Specifica del tipo di risposta

3) Richiesta di query

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

Possiamo usare questa classe AuthHttpInterceptor come provider per gli 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 {
}

8

Meglio tardi che mai ... =)

Puoi prendere il concetto di esteso BaseRequestOptions(da qui https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options ) e aggiornare le intestazioni "al volo "(non solo nel costruttore). È possibile utilizzare l'override della proprietà "header" di getter / setter in questo modo:

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

piccolo aggiornamento: per prestazioni migliori potresti considerare di spostare tutte le intestazioni statiche (come 'Content-Type') sul costruttore
Александр Ильинский

7

Ecco come ho fatto per impostare il token con ogni richiesta.

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

E registrati in app.module.ts

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule
    ],
    providers: [
        { provide: RequestOptions, useClass: CustomRequestOptions }
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

6

Ecco una versione migliorata della risposta accettata, aggiornata per 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());
    }
}

Naturalmente dovrebbe essere esteso a metodi come deletee putse necessario (non ho ancora bisogno di loro a questo punto del mio progetto).

Il vantaggio è che c'è meno codice duplicato nei metodi get/ post/ ....

Si noti che nel mio caso utilizzo i cookie per l'autenticazione. Avevo bisogno dell'intestazione per i18n (l' Accept-Languageintestazione) perché molti valori restituiti dalla nostra API sono tradotti nella lingua dell'utente. Nella mia app il servizio i18n contiene la lingua attualmente selezionata dall'utente.


come hai fatto a far sì che tslint ignorasse le intestazioni?
Winnemucca,

5

Che ne dici di mantenere un servizio separato come segue

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

e quando lo chiami da un altro posto usa this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

e vedrai l'intestazione aggiunta, ad esempio: - Autorizzazione come segue

inserisci qui la descrizione dell'immagine


5

Dopo alcune indagini, ho scoperto che il modo finale e più semplice è estendere quello BaseRequestOptionsche preferisco.
Di seguito sono riportati i modi in cui ho provato a rinunciare per qualche motivo:
1. estendere BaseRequestOptionse aggiungere intestazioni dinamiche constructor(). Non può funzionare se eseguo il login. Sarà creato una volta. Quindi non è dinamico.
2. estendere Http. Stesso motivo di cui sopra, non riesco ad aggiungere intestazioni dinamiche in constructor(). E se riscrivo il request(..)metodo e imposto le intestazioni, in questo modo:

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

Devi solo sovrascrivere questo metodo, ma non tutti i metodi get / post / put.

3.E la mia soluzione preferita è estendere BaseRequestOptionse sovrascrivere 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;
 }
}

questa merge()funzione verrà chiamata per ogni richiesta.


Tra tutte le risposte fornite, questa è la risposta che ha attirato la mia attenzione poiché ho già cercato una soluzione basata sull'estensione BaseRequestOptions. Tuttavia, purtroppo, questo non ha funzionato per me. eventuali ragioni?
vigamage

farlo funzionare. questa soluzione va bene e ho avuto un problema nel mio server. Ho dovuto fare alcune configurazioni per le richieste pre-volo CORS. consultare questo link stackoverflow.com/a/43962690/3892439
vigamage

Come ti colleghi AuthRequestOptionsal resto dell'app? Ho provato a providersinserirlo nella sezione ma non ha fatto nulla.
Travis Parks,

È necessario sostituire il provider per RequestOptions, no BaseRequestOptions. angular.io/api/http/BaseRequestOptions
Travis Parks

Nella mia app estendo solo BaseRequestOptions e estende già RequestOptions. Quindi in app.module, dovresti impostare i provider:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn,

5

Anche se sto rispondendo molto tardi, ma se qualcuno è alla ricerca di una soluzione più semplice.

Possiamo usare angular2-jwt. angular2-jwt è utile collegare automaticamente un token Web JSON (JWT) come intestazione di autorizzazione quando si effettuano richieste HTTP da un'app Angular 2.

Siamo in grado di impostare intestazioni globali con un'opzione di configurazione avanzata

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

E invio per richiesta token come

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

sarebbe utile andare su github.com/auth0/angular2-jwt#installation e adattare questa risposta usando la loro guida all'installazione
Zuriel,

4

Mi piace l'idea di sostituire le opzioni predefinite, questa sembra una buona soluzione.

Tuttavia, se stai per estendere la Httpclasse. Assicurati di leggere tutto!

Alcune risposte qui mostrano in realtà un sovraccarico errato del request()metodo, che potrebbe portare a errori difficili da individuare e comportamenti strani. Mi sono imbattuto in questo io stesso.

Questa soluzione si basa sull'implementazione del request()metodo in angolare 4.2.x, ma dovrebbe essere compatibile con il futuro:

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;

  }

}

Si noti che sto importando la classe originale in questo modo import { Http as NgHttp } from '@angular/http';per evitare conflitti di nomi.

Il problema affrontato qui è che il request()metodo ha due firme di chiamata diverse. Quando l' Requestoggetto viene passato anziché l'URL string, l' optionsargomento viene ignorato da Angular. Quindi entrambi i casi devono essere gestiti correttamente.

Ed ecco l'esempio di come registrare questa classe sovrascritta con il contenitore DI:

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

Con tale approccio è possibile iniettare la Httpclasse normalmente, ma la classe sovrascritta verrà invece iniettata magicamente. Ciò consente di integrare facilmente la soluzione senza modificare altre parti dell'applicazione (polimorfismo in azione).

Basta aggiungere httpProvideralla providersproprietà dei metadati del modulo.


1

Il più semplice di tutti

Crea un config.tsfile

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

Quindi sul tuo service, basta importare il config.tsfile

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

Penso che sia stato il più semplice e il più sicuro.


0

Ci sono stati alcuni cambiamenti per la versione 2.0.1 e successive:

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

Non funziona, l'ho provato da solo. Non viene chiamato altro che aggiornamento.
Phil

0

Sono stato in grado di scegliere una soluzione più semplice> Aggiungi una nuova intestazione alle opzioni predefinite unisci o carica tramite la tua funzione di acquisizione API (o altro).

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

Ovviamente puoi esternare queste intestazioni nelle opzioni predefinite o in qualsiasi altra cosa nella tua classe. Questo è nell'API di classe di esportazione api.ts @Injectable () generata da Ionic {}

È molto veloce e funziona per me. Non volevo il formato json / ld.


-4

Puoi utilizzare canActivenei tuoi percorsi, in questo modo:

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

Tratto da: https://auth0.com/blog/angular-2-authentication

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.