Angulaire - Définir des en-têtes pour chaque demande

334

Je dois définir des en-têtes d'autorisation après la connexion de l'utilisateur, pour chaque demande ultérieure.


Pour définir des en-têtes pour une demande particulière,

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

Référence

Mais il ne serait pas possible de définir manuellement les en-têtes de demande pour chaque demande de cette manière.

Comment définir les en-têtes définis une fois que l'utilisateur s'est connecté et supprimer également ces en-têtes à la déconnexion?

Avijit Gupta
la source

Réponses:

379

Pour répondre, vous vous demandez si vous pourriez fournir un service qui enveloppe l' Httpobjet d' origine d'Angular. Quelque chose comme décrit ci-dessous.

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

Et au lieu d'injecter l' Httpobjet, vous pouvez injecter celui-ci ( 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 );
    });
  }
}

Je pense aussi que quelque chose pourrait être fait en utilisant plusieurs fournisseurs pour la Httpclasse en fournissant votre propre classe en étendant celle- Httpci ... Voir ce lien: http://blog.oughttram.io/angular2/2015/11/23/multi-providers -en-angulaire-2.html .

Thierry Templier
la source
1
où est 'this.http = http;' vient, je crois que nous devons le déclarer avant d'utiliser?
co2f2e
1
En-têtes angulaires (fonctions set & append) "normalisent" la clé de l'en-tête et la rendent en minuscules. De Headers.d.ts: // "Les jeux de caractères HTTP sont identifiés par des jetons insensibles à la casse" // Spec at tools.ietf.org/html/rfc2616 Pour ceux qui n'ont pas de backend qui fonctionne selon la spécification; voici un contournement: let headersMap = .get (options, 'headers._headersMap', new Map ()); headersMap.set ('Authorization', [ .replace ( Bearer ${token}, / \ "/ g, '')]);
sangress
@DiegoUnanue J'utilise la version finale d'Angular 2 et les travaux d'implémentation de Thierry. Remplacez simplement 'angular2' par '@angular' dans les instructions d'importation.
f123
Mark Pieszak - dois-je inclure des fournisseurs pour HttpClient?
user3127109
TS lance maintenant une erreur: `Argument de type '{headers: Headers; } 'n'est pas assignable au paramètre de type' RequestOptionsArgs'`
elporfirio
142

Les intercepteurs HTTP sont désormais disponibles via le nouveau HttpClientdepuis @angular/common/http, à partir des versions Angular 4.3.x et au-delà .

Il est assez simple d'ajouter un en-tête pour chaque demande maintenant:

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

Il y a un principe d'immuabilité , c'est la raison pour laquelle la demande doit être clonée avant de mettre quelque chose de nouveau dessus.

Comme l'édition des en-têtes est une tâche très courante, il existe en fait un raccourci pour cela (lors du clonage de la demande):

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

Après avoir créé l'intercepteur, vous devez l'enregistrer à l'aide de la HTTP_INTERCEPTORSfourniture.

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}
Edmundo Rodrigues
la source
J'ai implémenté cela et quand je fais ng serve, je peux voir les en-têtes de demande, mais quand je fais ng b prod et que je déploie à l'intérieur d'un tomcat, je ne vois pas les en-têtes ... en utilisant spring-boot, où sont passés les en-têtes?
naoru
1
Je ne sais pas si c'est parce que je travaille avec une API de nœud Express, mais cela ne fonctionne pas pour moi, même avec la documentation Angular officielle. : /
Maxime Lafarie
ERREUR TypeError: CreateListFromArrayLike appelé sur un non-objet
DAG
1
Comment injecteriez-vous quelque chose dans HttpInterceptor?
zaitsman
J'ai implémenté ces mêmes choses mais dans l'en-tête de demande PUT et DELETE API ne fonctionne pas pour moi.
Pooja
78

L'extension BaseRequestOptionspeut être très utile dans ce scénario. Découvrez le code suivant:

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

Cela devrait inclure «My-Custom-Header» dans chaque appel.

Mettre à jour:

Pour pouvoir modifier l'en-tête à tout moment au lieu du code ci-dessus, vous pouvez également utiliser le code suivant pour ajouter un nouvel en-tête:

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

pour supprimer vous pouvez faire

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

Il existe également une autre fonction que vous pouvez utiliser pour définir la valeur:

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

La solution ci-dessus n'est toujours pas complètement valide dans un contexte dactylographié. _defaultHeaders est protégé et n'est pas censé être utilisé comme ça. Je recommanderais la solution ci-dessus pour une solution rapide, mais pour longtemps, il vaut mieux écrire votre propre wrapper autour des appels http qui gère également l'auth. Prenez l'exemple suivant de auth0 qui est meilleur et propre.

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

Mise à jour - juin 2018 Je vois beaucoup de gens opter pour cette solution mais je conseillerais le contraire. L'ajout d'en-tête au niveau mondial enverra un jeton d'authentification à chaque appel d'API sortant de votre application. Ainsi, les appels api allant à des plugins tiers comme intercom ou zendesk ou tout autre api porteront également votre en-tête d'autorisation. Cela pourrait entraîner un gros défaut de sécurité. Donc, à la place, utilisez l'intercepteur globalement mais vérifiez manuellement si l'appel sortant est vers le point de terminaison api de votre serveur ou non, puis attachez l'en-tête d'authentification.

anit
la source
1
this.http._defaultOptions.headers.delete ('My-Custom-Header') Ainsi, le processus ci-dessus peut être raccourci en suivant le code this.http._defaultOptions.headers.append ('My-New-Custom-Header', 'newvalue ')
anit
2
@Dinistro oui, maintenant je ne recommanderais pas de le faire. J'ai dû trouver cette solution de contournement en raison des limitations de la bêta angulaire et de mon habitude de contrôler le flux d'authentification à l'échelle mondiale. Mais je crois que pour l'instant github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts a une solution meilleure et propre.
anit
1
Le problème de l'utilisation de BaseRequestOptions est que son constructeur n'est exécuté qu'une seule fois dans la durée de vie de l'application dans le navigateur. Donc, si vous voulez changer la valeur de l'en-tête pendant le temps (par exemple, csrf_token), vous ne pouvez pas le faire de cette manière (même remplacer la méthode de fusion dans cette classe n'aide pas :()
Kamil Kiełczewski
1
Le problème est que si vous utilisez un wrapper, les bibliothèques tierces qui accèdent directement à HTTP doivent être réécrites pour l'utiliser. Je ne sais toujours pas comment contourner cela. Un intercepteur est vraiment nécessaire. Je ne sais pas si quelqu'un connaît une meilleure façon.
Piotr Stulinski
6
Salut, dans angular4 _defaultOptionsest protégé donc ne peut pas être appelé du service
Andurit
24

Même si je réponds très tard, cela pourrait aider quelqu'un d'autre. Pour injecter des en-têtes à toutes les requêtes quand il @NgModuleest utilisé, on peut faire ce qui suit:

(J'ai testé cela dans 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');
    }
}

Maintenant, @NgModuleprocédez comme suit:

@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]
})
Shashank Agrawal
la source
4
vous avez besoin de @Injectable et définissez des en-têtes dans la classe, j'ai testé avec succès par @Injectable () la classe d'exportation CustomRequestOptions étend BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack
eh bien, je l'ai fait dans 2.0.0, je n'ai pas vérifié 2.0.1
EasonBlack
Remarque importante ici, j'ai rencontré un problème où il était impossible d'injecter quoi que ce soit CustomRequestOptionsmême lors de l'utilisation de @ Inject / @ Injectable. La solution que j'ai réalisée était de prolonger RequestOptions, non BaseRequestOptions. Fournir BaseRequestOptionsne fonctionnera pas, mais étendre à la RequestOptionsplace fait fonctionner DI à nouveau.
parlement
5
Cette solution est simple, mais si l'utilisateur se déconnecte et se reconnecte et que son jeton change - cela ne fonctionnera plus, car l'en- Authorizationtête n'est défini qu'une seule fois lors du lancement de l'application.
Alex Paramonov
Oui, corrigez @AlexeyVParamonov. Ceci n'est utile que si le jeton est défini une fois. Sinon, nous écrirons les intercepteurs pour le cas comme vous l'avez dit.
Shashank Agrawal
15

Dans Angular 2.1.2j'ai approché cela en étendant le Http angulaire:

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

puis dans mes fournisseurs d'applications, j'ai pu utiliser une usine personnalisée pour fournir «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(){

  }
}

maintenant, je n'ai pas besoin de déclarer chaque méthode Http et httpje peux l'utiliser normalement dans toute mon application.

jonnie
la source
Cette réponse a fonctionné le mieux pour moi car j'ai été en mesure de filtrer l'URL de mon serveur api et d'ajouter uniquement le jeton d'authentification aux appels qui lui sont passés. J'ai changé la demande en: 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)}
Chris Holcomb
Dans mon cas, les informations d'identification et les en-têtes ont été extraits du paramètre url dans la méthode de demande. J'ai changé le code comme ceci: 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)}
Argnist
La request()méthode que vous surchargez a deux signatures d'appel et la optionspropriété est utilisée uniquement lorsqu'elle est urlspécifiée en tant que chaîne. Dans le cas où urlest une instance de Request, la optionspropriété est simplement ignorée. Cela pourrait entraîner des erreurs difficiles à détecter. Veuillez consulter ma réponse pour plus de détails.
Slava Fomin II
Notez que cette solution a quelques problèmes avec la plate-forme serveur . Il existe cependant des solutions pour l'éviter .
Alireza Mirian
Cela a fonctionné pour moi jusqu'à angulaire 4.2. 4.3 Dispose d'intercepteurs.
cabaji99
12

Créez une classe Http personnalisée en étendant le Httpfournisseur Angular 2 et remplacez simplement la méthode constructoret requestdans votre classe Http personnalisée. L'exemple ci-dessous ajoute un en- Authorizationtête dans chaque demande 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);
    };
  }
}

Configurez ensuite votre main app.module.tspour fournir le XHRBackendcomme ConnectionBackendfournisseur et le RequestOptionsà votre classe Http personnalisée:

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

Après cela, vous pouvez maintenant utiliser votre fournisseur http personnalisé dans vos services. Par exemple:

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

Voici un guide complet - http://adonespitogo.com/articles/angular-2-extending-http-provider/

Adones Pitogo
la source
Cette approche est bien adaptée pour utiliser un autre fournisseur de classe. Au lieu de «fournir: HttpService» comme vous l'avez dans votre module, vous pouvez utiliser à la place «fournir: Http» vous permettant de travailler avec Http comme vous le feriez habituellement.
Le Gilbert Arenas Dagger du
Comment puis-je ajouter des propriétés supplémentaires à cette classe http étendue? Par exemple, routeur: routeur ou tout autre service injectable personnalisé.
shafeequemat
@shafeequemat Vous ne pouvez pas faire cela en utilisant ceci. Vous pouvez définir une autre méthode dans votre classe http personnalisée, par exemple setRouter(router). Ou vous pouvez créer une autre classe et y injecter votre classe http personnalisée au lieu du contraire.
Adones Pitogo
9

Pour Angular 5 et supérieur, nous pouvons utiliser HttpInterceptor pour généraliser les opérations de demande et de réponse. Cela nous aide à éviter la duplication:

1) En-têtes communs

2) Spécification du type de réponse

3) Requête de requête

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

Nous pouvons utiliser cette classe AuthHttpInterceptor comme fournisseur pour les 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 {
}
Prachi
la source
8

Mieux vaut tard que jamais ... =)

Vous pouvez prendre le concept d'extension BaseRequestOptions(à partir d'ici https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options ) et actualiser les en-têtes "à la volée" "(pas seulement dans le constructeur). Vous pouvez utiliser le remplacement de la propriété geters / setter "headers" comme ceci:

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 };
Александр Ильинский
la source
petite mise à jour: pour de meilleures performances, vous pouvez envisager de déplacer tous les en-têtes statiques (comme «Content-Type») vers le constructeur
Александр Ильинский
7

C'est ainsi que je l'ai fait pour définir un jeton à chaque demande.

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

Et inscrivez-vous dans app.module.ts

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

Voici une version améliorée de la réponse acceptée, mise à jour pour 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());
    }
}

Bien sûr, il devrait être étendu pour des méthodes comme deleteet putsi nécessaire (je n'en ai pas encore besoin à ce stade de mon projet).

L'avantage est qu'il y a moins de code dupliqué dans les méthodes get/ post/ ....

Notez que dans mon cas, j'utilise des cookies pour l'authentification. J'avais besoin de l'en-tête pour i18n (l'en- Accept-Languagetête) car de nombreuses valeurs renvoyées par notre API sont traduites dans la langue de l'utilisateur. Dans mon application, le service i18n contient la langue actuellement sélectionnée par l'utilisateur.

Pierre Henry
la source
comment avez-vous réussi à faire en sorte que tslint ignore les en-têtes comme laissé?
Winnemucca
5

Que diriez-vous de conserver un service distinct comme suit

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

et lorsque vous appelez cela depuis un autre endroit, utilisez this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

et vous verrez l'en-tête ajouté, par exemple: - Autorisation comme suit

entrez la description de l'image ici

co2f2e
la source
5

Après quelques recherches, j'ai trouvé la finale et le moyen le plus simple est de prolonger BaseRequestOptionsce que je préfère.
Voici les façons dont j'ai essayé d'abandonner pour une raison quelconque:
1. étendre BaseRequestOptionset ajouter des en-têtes dynamiques constructor(). Cela ne peut pas fonctionner si je me connecte. Il sera créé une fois. Ce n'est donc pas dynamique.
2. étendre Http. Même raison que ci-dessus, je ne peux pas ajouter d'en-têtes dynamiques constructor(). Et si je réécris la request(..)méthode et que je définis des en-têtes, comme ceci:

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

Vous avez juste besoin d'écraser cette méthode, mais pas toutes les méthodes get / post / put.

3.Et ma solution préférée est d'étendre BaseRequestOptionset d'écraser 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;
 }
}

cette merge()fonction sera appelée pour chaque demande.

Mavlarn
la source
Parmi toutes les réponses données, c'est la réponse qui a retenu mon attention puisque j'ai déjà opté pour une solution qui est basée sur l'extension BaseRequestOptions. Malheureusement, cela n'a pas fonctionné pour moi. des raisons possibles?
vigamage
l'a fait fonctionner. cette solution est très bien et j'ai eu un problème sur mon serveur. J'ai dû faire quelques configurations pour les demandes de pré-vol CORS.
renvoyez
Comment vous connectez-vous AuthRequestOptionsau reste de l'application? J'ai essayé de mettre cela dans la providerssection mais cela n'a rien fait.
Travis Parks
Vous devez remplacer le fournisseur pour RequestOptions, non BaseRequestOptions. angular.io/api/http/BaseRequestOptions
Travis Parks
Dans mon application, j'étends simplement BaseRequestOptions, et cela étend déjà RequestOptions. Ensuite, dans app.module, vous devez définir les fournisseurs:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn
5

Bien que je réponde très tard, mais si quelqu'un cherche une solution plus simple.

Nous pouvons utiliser angular2-jwt. angular2-jwt est utile pour attacher automatiquement un jeton Web JSON (JWT) en tant qu'en-tête d'autorisation lors des requêtes HTTP à partir d'une application Angular 2.

Nous pouvons définir des en-têtes globaux avec une option de configuration avancée

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

Et l'envoi par jeton de demande comme

    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')
    );
}
KNimhan
la source
serait utile pour aller sur github.com/auth0/angular2-jwt#installation et adapter cette réponse en utilisant leur guide d'installation
Zuriel
4

J'aime l'idée de remplacer les options par défaut, cela semble être une bonne solution.

Cependant, si vous souhaitez étendre la Http classe. Assurez-vous de lire attentivement!

Certaines réponses ici montrent en fait une surcharge incorrecte de request() méthode, ce qui pourrait entraîner des erreurs difficiles à détecter et un comportement étrange. Je suis tombé dessus moi-même.

Cette solution est basée sur l' request()implémentation d'une méthode dans Angular 4.2.x, mais devrait être compatible avec l'avenir:

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;

  }

}

Notez que j'importe la classe d'origine de cette façon import { Http as NgHttp } from '@angular/http';afin d'éviter les conflits de noms.

Le problème abordé ici est que la request()méthode a deux signatures d'appel différentes. Lorsque l' Requestobjet est passé à la place de l'URL string, l' optionsargument est ignoré par Angular. Les deux cas doivent donc être traités correctement.

Et voici l'exemple de la façon d'enregistrer cette classe substituée avec le conteneur 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
  );
}

Avec une telle approche, vous pouvez injecter la Httpclasse normalement, mais votre classe surchargée sera injectée comme par magie. Cela vous permet d'intégrer facilement votre solution sans changer d'autres parties de l'application (polymorphisme en action).

Ajoutez simplement httpProviderà la providerspropriété de vos métadonnées de module.

Slava Fomin II
la source
1

Le plus simple de tous

Créer un config.tsfichier

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

Ensuite service, importez simplement le config.tsfichier

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

Je pense que c'était le plus simple et le plus sûr.

Joshua Fabillar
la source
0

Il y a eu quelques changements pour angular 2.0.1 et supérieur:

    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 { }
Carlos Casallas
la source
Ne fonctionne pas, j'ai essayé moi-même. Ne se fait appeler que pour se rafraîchir.
Phil
0

J'ai pu choisir une solution plus simple> Ajouter un nouvel en-tête aux options par défaut fusionner ou charger par votre fonction get (ou autre) api.

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

Bien sûr, vous pouvez externaliser ces en-têtes dans les options par défaut ou quoi que ce soit dans votre classe. C'est dans l'API de classe d'exportation api.ts @Injectable () générée par Ionic {}

C'est très rapide et ça marche pour moi. Je ne voulais pas du format json / ld.

Paul Leclerc
la source
-4

Vous pouvez utiliser canActivedans vos itinéraires, comme ceci:

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

Tiré de: https://auth0.com/blog/angular-2-authentication

À
la source