Winkel - Legen Sie für jede Anforderung Überschriften fest

334

Ich muss für jede nachfolgende Anforderung einige Autorisierungsheader festlegen, nachdem sich der Benutzer angemeldet hat.


So setzen Sie Header für eine bestimmte Anforderung:

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

Referenz

Es wäre jedoch nicht möglich, Anforderungsheader für jede Anforderung auf diese Weise manuell festzulegen.

Wie setze ich die Header, die festgelegt wurden, nachdem sich der Benutzer angemeldet hat, und entferne diese Header auch beim Abmelden?

Avijit Gupta
quelle

Antworten:

378

Zur Beantwortung Ihrer Frage könnten Sie einen Dienst bereitstellen, der das ursprüngliche HttpObjekt von Angular umschließt. So etwas wie unten beschrieben.

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

Und anstatt das HttpObjekt zu injizieren, können Sie dieses ( HttpClient) injizieren .

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

Ich denke auch, dass etwas mit mehreren Anbietern für die HttpKlasse getan werden könnte, indem Sie Ihre eigene Klasse bereitstellen, die die Httpeine erweitert ... Siehe diesen Link: http : //blog. Thoughtram.io/angular2/2015/11/23/multi-providers -in-angle-2.html .

Thierry Templier
quelle
1
wo ist 'this.http = http;' kommt von, ich glaube, wir müssen es deklarieren, bevor wir verwenden?
co2f2e
1
Winkel-Header (Funktionen zum Setzen und Anhängen) "normalisieren" den Header-Schlüssel und machen ihn in Kleinbuchstaben. Aus Headers.d.ts: // "HTTP-Zeichensätze werden durch Token ohne Berücksichtigung der Groß- und Kleinschreibung identifiziert" // Spezifikation unter tools.ietf.org/html/rfc2616 Für diejenigen, die kein Backend haben, das nach der Spezifikation funktioniert; Hier ist eine Umgehung: let headersMap = .get (Optionen, 'headers._headersMap', neue Map ()); headersMap.set ('Authorization', [ .replace ( Bearer ${token}, / \ "/ g, '')])
Sangress
@DiegoUnanue Ich verwende die endgültige Version von Angular 2 und Thierrys Implementierungsarbeiten. Ersetzen Sie einfach 'angle2' durch '@angular' in den import-Anweisungen.
f123
Mark Pieszak - sollte ich Anbieter für HttpClient einbeziehen?
user3127109
jetzt löst TS den Fehler aus: `Argument vom Typ '{headers: Headers; } 'kann nicht dem Parameter vom Typ' RequestOptionsArgs'` zugewiesen werden
elporfirio
142

HTTP Abfangjäger sind jetzt verfügbar über die neuen HttpClientaus @angular/common/http, als von Angular 4.3.x Versionen und darüber hinaus .

Es ist jetzt ziemlich einfach, für jede Anfrage einen Header hinzuzufügen:

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

Es gibt ein Prinzip der Unveränderlichkeit . Aus diesem Grund muss die Anforderung geklont werden, bevor etwas Neues darauf festgelegt wird.

Da das Bearbeiten von Headern eine sehr häufige Aufgabe ist, gibt es tatsächlich eine Verknüpfung dafür (beim Klonen der Anforderung):

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

Nachdem Sie den Interceptor erstellt haben, sollten Sie ihn mit dem HTTP_INTERCEPTORSProvide registrieren .

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}
Edmundo Rodrigues
quelle
Ich habe dies implementiert und wenn ich ng Serve mache, kann ich die Anforderungsheader sehen, aber wenn ich ng b prod mache und in einem Tomcat bereitstelle, sehe ich die Header nicht ... mit Spring-Boot, wohin gingen die Header?
Naoru
1
Ich weiß nicht, ob es daran liegt, dass ich mit einer Express-Knoten-API arbeite, aber es funktioniert nicht einmal mit dem offiziellen Angular-Dokument. : /
Maxime Lafarie
ERROR TypeError: CreateListFromArrayLike wird für Nichtobjekte aufgerufen
DAG
1
Wie würden Sie etwas in HttpInterceptor injizieren?
Zaitsman
Ich habe die gleichen Dinge implementiert, aber in PUT und DELETE funktioniert der API-Anforderungsheader für mich nicht.
Pooja
78

Das Erweitern BaseRequestOptionskann in diesem Szenario eine große Hilfe sein. Überprüfen Sie den folgenden 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 })
]);

Dies sollte in jedem Anruf 'My-Custom-Header' enthalten.

Aktualisieren:

Um den Header jederzeit anstelle des obigen Codes ändern zu können, können Sie auch den folgenden Code verwenden, um einen neuen Header hinzuzufügen:

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

zu löschen können Sie tun

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

Es gibt auch eine andere Funktion, mit der Sie den Wert einstellen können:

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

Die obige Lösung ist im Typoskript-Kontext immer noch nicht vollständig gültig. _defaultHeaders ist geschützt und sollte nicht so verwendet werden. Ich würde die obige Lösung für eine schnelle Lösung empfehlen, aber auf lange Sicht ist es besser, einen eigenen Wrapper um http-Aufrufe zu schreiben, der auch die Authentifizierung übernimmt. Nehmen Sie das folgende Beispiel aus auth0, das besser und sauberer ist.

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

Update - Juni 2018 Ich sehe viele Leute, die sich für diese Lösung entscheiden, aber ich würde anders raten. Wenn Sie den Header global anhängen, wird ein Authentifizierungstoken an jeden API-Aufruf gesendet , der von Ihrer App ausgeht. Die API-Aufrufe, die an Plugins von Drittanbietern wie Intercom, Zendesk oder eine andere API gesendet werden, enthalten also auch Ihren Autorisierungsheader. Dies kann zu einer großen Sicherheitslücke führen. Verwenden Sie stattdessen den Interceptor global, prüfen Sie jedoch manuell, ob der ausgehende Anruf auf den API-Endpunkt Ihres Servers gerichtet ist oder nicht, und fügen Sie dann den Auth-Header hinzu.

anit
quelle
1
this.http._defaultOptions.headers.delete ('My-Custom-Header') Der obige Prozess kann also durch folgenden Code verkürzt werden: this.http._defaultOptions.headers.append ('My-New-Custom-Header', 'newvalue' ')
anit
2
@ Dinistro ja, jetzt werde ich mir nicht empfehlen, dies zu tun. Ich musste diese Problemumgehung aufgrund von Beta-Einschränkungen und meiner Gewohnheit, den Authentifizierungsfluss global zu steuern, finden. Aber ich glaube, dass github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts vorerst eine bessere und saubere Lösung hat.
anit
1
Das Problem bei der Verwendung von BaseRequestOptions besteht darin, dass der Konstruktor nur einmal in der Anwendungslebensdauer im Browser ausgeführt wird. Wenn Sie also den Header-Wert während der Zeit ändern möchten (z. B. csrf_token), können Sie dies nicht auf diese Weise tun (selbst das Überschreiben der Zusammenführungsmethode in dieser Klasse hilft nicht :()
Kamil Kiełczewski
1
Problem ist, dass, wenn Sie einen Wrapper verwenden, Bibliotheken von Drittanbietern, die direkt auf HTTP zugreifen, neu geschrieben werden müssen, um ihn zu verwenden. Ich weiß immer noch nicht, wie ich das umgehen soll. Ein Abfangjäger wird wirklich benötigt. Ich bin mir nicht sicher, ob jemand einen besseren Weg kennt.
Piotr Stulinski
6
Hallo, in angle4 _defaultOptionsist geschützt, kann also nicht aus dem Dienst gerufen werden
Andurit
24

Ich antworte zwar sehr spät, aber es könnte jemand anderem helfen. Um allen Anforderungen bei @NgModuleVerwendung Header hinzuzufügen , kann Folgendes ausgeführt werden:

(Ich habe dies in Angular 2.0.1 getestet.)

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

Gehen @NgModuleSie nun wie folgt vor:

@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
quelle
4
Sie benötigen @Injectable und definieren Header in der Klasse. Ich habe sie erfolgreich mit der Exportklasse @Injectable () getestet. CustomRequestOptions erweitert BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack
Nun
Wichtiger Hinweis hier stieß ich auf ein Problem, bei dem es unmöglich war, etwas zu injizieren, CustomRequestOptionsselbst wenn @ Inject / @ Injectable verwendet wurde. Die Lösung, die ich erkannte, war zu erweitern RequestOptions, nicht BaseRequestOptions. Das Bereitstellen BaseRequestOptionsfunktioniert nicht, aber durch das Erweitern RequestOptionsfunktioniert DI wieder.
Parlament
5
Diese Lösung ist einfach, aber wenn sich der Benutzer abmeldet und wieder anmeldet und sich sein Token ändert, funktioniert dies nicht mehr, da der AuthorizationHeader bei der Anwendungsinitialisierung nur einmal festgelegt wird.
Alex Paramonov
Ja, richtig @AlexeyVParamonov. Dies ist nur nützlich, wenn das Token einmal gesetzt wird. Andernfalls schreiben wir die Abfangjäger für den Fall, wie Sie gesagt haben.
Shashank Agrawal
15

In Angular 2.1.2Ich näherte mich dem durch Erweitern des eckigen 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)
  }
}

Dann konnte ich in meinen App-Anbietern eine benutzerdefinierte Factory verwenden, um "HTTP" bereitzustellen.

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(){

  }
}

Jetzt muss ich nicht jede HTTP-Methode deklarieren und kann sie httpin meiner gesamten Anwendung wie gewohnt verwenden.

Jonnie
quelle
Diese Antwort funktionierte am besten für mich, da ich die URL zu meinem API-Server filtern und das Auth-Token nur zu Anrufen hinzufügen konnte. Ich habe die Anfrage geändert in: 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, Optionen)}
Chris Holcomb
In meinem Fall wurden withCredentials und Header aus dem URL-Parameter in der Anforderungsmethode übernommen. Ich habe den Code folgendermaßen geändert: request (url: string | Request, options?: RequestOptionsArgs): Observable <Response> {options = this._setCustomHeaders (options); if (typeof (url) === "object") {(<Anforderung> url) .withCredentials = options.withCredentials; (<Anforderung> URL) .headers = options.headers; } return super.request (URL, Optionen)}
Argnist
Die request()Methode, die Sie überladen, hat zwei Aufrufsignaturen und die optionsEigenschaft wird nur verwendet, wenn urlsie als Zeichenfolge angegeben wird. Wenn urles sich um eine Instanz von handelt Request, wird die optionsEigenschaft einfach ignoriert. Dies kann zu schwer zu fassenden Fehlern führen. Bitte sehen Sie meine Antwort für weitere Details.
Slava Fomin II
Beachten Sie, dass diese Lösung einige Probleme mit der Serverplattform hat . Es gibt jedoch Problemumgehungen, um dies zu vermeiden .
Alireza Mirian
Dies funktionierte bei mir bis Winkel 4.2. 4.3 Hat Abfangjäger.
cabaji99
12

Erstellen Sie eine benutzerdefinierte HTTP-Klasse, indem Sie den Angular 2- HttpAnbieter erweitern, und überschreiben Sie einfach die Methode constructorund requestin Ihrer benutzerdefinierten HTTP-Klasse. Im folgenden Beispiel wird Authorizationjeder http-Anforderung ein Header hinzugefügt.

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

Konfigurieren Sie dann Ihr Hauptmenü so app.module.ts, dass es das XHRBackendals ConnectionBackendAnbieter und das RequestOptionsfür Ihre benutzerdefinierte HTTP-Klasse bereitstellt :

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

Danach können Sie Ihren benutzerdefinierten http-Anbieter in Ihren Diensten verwenden. Zum Beispiel:

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

Hier ist eine umfassende Anleitung - http://adonespitogo.com/articles/angular-2-extending-http-provider/

Adones Pitogo
quelle
Dieser Ansatz eignet sich gut für die Verwendung eines alternativen Klassenanbieters. Anstelle von "bereitstellen: HTTP-Service", wie Sie es in Ihrem Modul haben, können Sie stattdessen "Bereitstellen: HTTP" verwenden, damit Sie wie gewohnt mit HTTP arbeiten können.
Der Gilbert Arenas Dolch
Wie kann ich dieser erweiterten http-Klasse zusätzliche Eigenschaften hinzufügen? Beispiel: Router: Router oder benutzerdefinierte injizierbare Dienste.
shafeequemat
@shafeequemat Mit dem kannst du das nicht machen. Sie können beispielsweise eine andere Methode in Ihrer benutzerdefinierten http-Klasse definieren setRouter(router). Oder Sie können eine andere Klasse erstellen und dort Ihre benutzerdefinierte http-Klasse anstelle des Gegenteils einfügen.
Adones Pitogo
9

Für Angular 5 und höher können wir HttpInterceptor zum Verallgemeinern der Anforderungs- und Antwortoperationen verwenden. Dies hilft uns, Doppelarbeit zu vermeiden:

1) Gemeinsame Überschriften

2) Angeben des Antworttyps

3) Anfrage abfragen

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

Wir können diese AuthHttpInterceptor-Klasse als Provider für die HttpInterceptors verwenden:

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
quelle
8

Besser spät als nie ... =)

Sie können das Konzept der erweiterten OptionBaseRequestOptions (von hier aus https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options ) übernehmen und die Header "on the fly" aktualisieren "(nicht nur im Konstruktor). Sie können die Überschreibung der Getter / Setter-Eigenschaft "headers" wie folgt verwenden:

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 };
Александр Ильинский
quelle
kleines Update: Um eine bessere Leistung zu erzielen, können Sie alle statischen Header (wie 'Content-Type') in den Konstruktor verschieben
Александр Ильинский
7

So habe ich das Token bei jeder Anfrage gesetzt.

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

Und registrieren Sie sich in app.module.ts

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

Hier ist eine verbesserte Version der akzeptierten Antwort, die für Angular2 final aktualisiert wurde:

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

Natürlich sollte es für Methoden wie deleteund putbei Bedarf erweitert werden (ich brauche sie zu diesem Zeitpunkt in meinem Projekt noch nicht).

Der Vorteil ist, dass die get/ post/ ... -Methoden weniger doppelten Code enthalten .

Beachten Sie, dass ich in meinem Fall Cookies zur Authentifizierung verwende. Ich brauchte den Header für i18n (den Accept-LanguageHeader), da viele von unserer API zurückgegebene Werte in die Sprache des Benutzers übersetzt werden. In meiner App enthält der i18n-Dienst die aktuell vom Benutzer ausgewählte Sprache.

Pierre Henry
quelle
Wie haben Sie tslint dazu gebracht, Header als let zu ignorieren?
Winnemucca
5

Wie wäre es mit einem separaten Service wie folgt?

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

und wenn Sie dies von einem anderen Ort aus anrufen, verwenden Sie this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

und Sie sehen den hinzugefügten Header zB: - Autorisierung wie folgt

Geben Sie hier die Bildbeschreibung ein

co2f2e
quelle
5

Nach einigen Nachforschungen fand ich den endgültigen und einfachsten Weg, ihn zu erweitern BaseRequestOptions.
Im Folgenden habe ich versucht, aus irgendeinem Grund aufzugeben:
1. Erweitern Sie BaseRequestOptionsund fügen Sie dynamische Header hinzu constructor(). Es kann nicht funktionieren, wenn ich mich anmelde. Es wird einmal erstellt. Es ist also nicht dynamisch.
2. verlängern Http. Aus dem gleichen Grund wie oben kann ich keine dynamischen Header hinzufügen constructor(). Und wenn ich die request(..)Methode neu schreibe und Header wie folgt setze:

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

Sie müssen diese Methode nur überschreiben, aber nicht alle get / post / put-Methoden.

3.Und meine bevorzugte Lösung ist Erweitern BaseRequestOptionsund Überschreiben 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;
 }
}

Diese merge()Funktion wird bei jeder Anfrage aufgerufen.

Mavlarn
quelle
Unter all den gegebenen Antworten ist dies die Antwort, die meine Aufmerksamkeit auf sich gezogen hat, da ich mich bereits für eine Lösung entschieden habe, die auf der Erweiterung basiert BaseRequestOptions. Leider hat dies bei mir nicht funktioniert. irgendwelche möglichen Gründe?
Vigamage
habe es zum Laufen gebracht. Diese Lösung ist in Ordnung und ich hatte ein Problem auf meinem Server. Ich musste einige Konfigurationen für CORS-Anfragen vor dem Flug vornehmen. Verweisen Sie auf diesen Link stackoverflow.com/a/43962690/3892439
Vigamage
Wie knüpfen Sie AuthRequestOptionsan den Rest der App an? Ich habe versucht, dies in den providersAbschnitt aufzunehmen, aber es hat nichts getan.
Travis Parks
Sie müssen den Anbieter für RequestOptionsnicht überschreiben BaseRequestOptions. angle.io/api/http/BaseRequestOptions
Travis Parks
In meiner App erweitere ich nur BaseRequestOptions und es erweitert bereits RequestOptions. Dann sollten Sie in app.module die Anbieter einstellen:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn
5

Ich antworte zwar sehr spät, aber wenn jemand nach einer einfacheren Lösung sucht.

Wir können angle2-jwt verwenden. angle2-jwt ist nützlich, um automatisch ein JSON-Web-Token (JWT) als Autorisierungsheader anzuhängen, wenn HTTP-Anforderungen von einer Angular 2-App gestellt werden.

Wir können globale Header mit erweiterter Konfigurationsoption festlegen

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

Und Senden pro Anfrage Token wie

    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
quelle
Es wäre hilfreich, zu github.com/auth0/angular2-jwt#installation zu gehen und diese Antwort mithilfe der Installationsanleitung anzupassen
Zuriel
4

Ich mag die Idee, Standardoptionen zu überschreiben, dies scheint eine gute Lösung zu sein.

Wenn Sie jedoch die HttpKlasse erweitern möchten. Lesen Sie dies unbedingt durch!

Einige Antworten hier zeigen tatsächlich eine falsche Überladung der request()Methode, was zu schwer zu fassenden Fehlern und seltsamem Verhalten führen kann. Ich bin selbst darauf gestoßen.

Diese Lösung basiert auf der request()Methodenimplementierung in Angular 4.2.x, sollte jedoch zukunftskompatibel sein:

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;

  }

}

Beachten Sie, dass ich die ursprüngliche Klasse auf diese Weise importiere import { Http as NgHttp } from '@angular/http';, um Namenskonflikte zu vermeiden.

Das hier angesprochene Problem besteht darin, dass die request()Methode zwei verschiedene Anrufsignaturen aufweist. Wenn ein RequestObjekt anstelle der URL übergeben wird string, wird das optionsArgument von Angular ignoriert. Beide Fälle müssen also ordnungsgemäß behandelt werden.

Und hier ist das Beispiel, wie diese überschriebene Klasse im DI-Container registriert wird:

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

Mit einem solchen Ansatz können Sie die HttpKlasse normal injizieren , aber Ihre überschriebene Klasse wird stattdessen magisch injiziert. Auf diese Weise können Sie Ihre Lösung einfach integrieren, ohne andere Teile der Anwendung zu ändern (Polymorphismus in Aktion).

Fügen Sie einfach httpProviderdie providersEigenschaft Ihrer Modul-Metadaten hinzu.

Slava Fomin II
quelle
1

Das einfachste von allen

Erstellen Sie eine config.tsDatei

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

Dann serviceimportieren Sie einfach die config.tsDatei

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

Ich denke, es war das einfachste und sicherste.

Joshua Fabillar
quelle
0

Es gab einige Änderungen für Winkel 2.0.1 und höher:

    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
quelle
Funktioniert nicht, habe es selbst versucht. Wird nur zum Aktualisieren aufgerufen.
Phil
0

Ich kann eine einfachere Lösung auswählen> Fügen Sie den Standardoptionen neue Header hinzu, die von Ihrer API-Get-Funktion (oder einer anderen Funktion) zusammengeführt oder geladen werden.

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

Natürlich können Sie diese Header in Standardoptionen oder was auch immer in Ihrer Klasse externalisieren. Dies ist in der von Ionic generierten API-API {} von @.jectable () der Exportklasse {}

Es ist sehr schnell und es funktioniert für mich. Ich wollte kein json / ld-Format.

Paul Leclerc
quelle
-4

Sie können canActivein Ihren Routen Folgendes verwenden:

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

Entnommen aus: https://auth0.com/blog/angular-2-authentication

BEIM
quelle