Kątowy - Ustaw nagłówki dla każdego żądania

334

Muszę ustawić niektóre nagłówki autoryzacji po zalogowaniu użytkownika dla każdego kolejnego żądania.


Aby ustawić nagłówki dla konkretnego żądania,

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

Odniesienie

Nie byłoby jednak możliwe ręczne ustawienie nagłówków żądań dla każdego żądania w ten sposób.

Jak ustawić zestaw nagłówków po zalogowaniu się użytkownika, a także usunąć te nagłówki po wylogowaniu?

Avijit Gupta
źródło

Odpowiedzi:

379

Aby odpowiedzieć, pytasz, czy możesz zapewnić usługę, która otacza oryginalny Httpobiekt z Angulara. Coś jak opisano poniżej.

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

I zamiast wstrzykiwać Httpobiekt, możesz wstrzyknąć ten ( 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 );
    });
  }
}

Myślę też, że można by coś zrobić, korzystając z usług wielu dostawców dla tej Httpklasy, udostępniając własną klasę rozszerzającą tę Http... Zobacz ten link: http://blog.thoughtram.io/angular2/2015/11/23/multi-providers -in-angular-2.html .

Thierry Templier
źródło
1
gdzie jest „this.http = http;” pochodzi, uważam, że musimy to zadeklarować przed użyciem?
co2f2e,
1
kątowe Nagłówki (funkcje ustawiania i dołączania) „normalizują” klucz nagłówka i sprawiają, że ma on małe litery. From Headers.d.ts: // „Zestawy znaków HTTP są identyfikowane za pomocą tokenów bez rozróżniania wielkości liter” // Spec at tools.ietf.org/html/rfc2616 Dla tych, którzy nie mają zaplecza działającego według specyfikacji; oto obejście: pozwól headersMap = .get (opcje, 'headers._headersMap', nowa Map ()); headersMap.set ('Autoryzacja', [ .replace ( Bearer ${token}, / \ "/ g, '')]);
sangress
@DiegoUnanue Używam ostatecznej wersji Angulara 2 i prac implementacyjnych Thierry'ego. Po prostu zamień „angular2” na „@angular” w instrukcjach importu.
f123
Mark Pieszak - czy powinienem dołączyć dostawców dla HttpClient?
user3127109,
teraz TS zgłasza błąd: `Argument typu '{headers: Headers; } ”nie można przypisać do parametru typu„ RequestOptionsArgs ”
elporfirio
142

HTTP przechwytujące są teraz dostępne poprzez nowy HttpClientz @angular/common/http, jak kątowego wersji 4.3.x i poza nim .

Dodanie nagłówka do każdego żądania jest teraz bardzo proste:

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

Istnieje zasada niezmienności , dlatego powód należy sklonować przed umieszczeniem na nim czegoś nowego.

Ponieważ edytowanie nagłówków jest bardzo częstym zadaniem, istnieje do niego skrót (podczas klonowania żądania):

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

Po utworzeniu przechwytywacza powinieneś zarejestrować go za pomocą metody HTTP_INTERCEPTORSdostarczania.

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}
Edmundo Rodrigues
źródło
Zaimplementowałem to i kiedy robię serwer ng, widzę nagłówki żądań, jednak kiedy robię ng b prod i wdrażam wewnątrz tomcat, nie widzę nagłówków ... używając Spring-boot, gdzie poszły nagłówki?
naoru
1
Nie wiem, czy to dlatego, że pracuję z interfejsem API węzła Express, ale nie działa dla mnie nawet z oficjalnym dokumentem Angular. : /
Maxime Lafarie,
BŁĄD TypeError: CreateListFromArrayLike wywołany bez obiektu
DAG
1
Jak wstrzyknąłbyś cokolwiek do HttpInterceptor?
zaitsman
Zaimplementowałem te same rzeczy, ale nagłówek żądania PUT i DELETE API nie działa dla mnie.
Pooja
78

Rozszerzanie BaseRequestOptionsmoże być bardzo pomocne w tym scenariuszu. Sprawdź następujący kod:

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

Powinno to obejmować „My-Custom-Header” w każdym połączeniu.

Aktualizacja:

Aby móc zmienić nagłówek w dowolnym momencie zamiast powyższego kodu, możesz również użyć następującego kodu, aby dodać nowy nagłówek:

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

możesz usunąć

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

Istnieje również inna funkcja, której można użyć do ustawienia wartości:

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

Powyższe rozwiązanie nadal nie jest całkowicie poprawne w kontekście maszynopisu. _defaultHeaders jest chroniony i nie powinien być używany w ten sposób. Poleciłbym powyższe rozwiązanie w celu szybkiej naprawy, ale na dłuższą metę lepiej jest napisać własne opakowanie wokół wywołań http, które również obsługuje uwierzytelnianie. Weź następujący przykład z auth0, który jest lepszy i czysty.

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

Aktualizacja - czerwiec 2018 r. Widzę wielu ludzi wybierających to rozwiązanie, ale radziłbym inaczej. Globalne dołączanie nagłówka spowoduje wysłanie tokena uwierzytelniającego do każdego wywołania interfejsu API wychodzącego z aplikacji. Tak więc wywołania interfejsu API przechodzące do wtyczek stron trzecich, takich jak domofon, zendesk lub jakikolwiek inny interfejs API, również zawierają nagłówek autoryzacji. Może to spowodować poważną lukę w zabezpieczeniach. Zamiast tego używaj globalnie przechwytywacza, ale sprawdź ręcznie, czy połączenie wychodzące jest w kierunku punktu końcowego interfejsu API serwera, czy nie, a następnie dołącz nagłówek uwierzytelniania.

anit
źródło
1
this.http._defaultOptions.headers.delete („My-Custom-Header”) Tak więc powyższy proces można skrócić, wykonując kod this.http._defaultOptions.headers.append („My-New-Custom-Header”, „newvalue” ')
anit
2
@Dinistro tak, teraz nie polecam sobie tego robić. Musiałem wymyślić to obejście ze względu na ograniczenia kątowe beta i mój nawyk globalnego kontrolowania przepływu autoryzacji. Ale wierzę, że na razie github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts ma lepsze i czyste rozwiązanie.
anit
1
Problem z użyciem BaseRequestOptions polega na tym, że jego konstruktor jest uruchamiany tylko raz w życiu aplikacji w przeglądarce. Więc jeśli chcesz zmienić wartość nagłówka w czasie (np. Csrf_token), nie możesz tego zrobić w ten sposób (nawet przesłonięcie metody scalania w tej klasie nie pomaga :()
Kamil Kiełczewski
1
Problem polega na tym, że jeśli korzystasz z opakowania zewnętrznych bibliotek, które uzyskują bezpośredni dostęp do HTTP, trzeba go ponownie zapisać, aby z niego korzystać. Nadal nie wiem jak to obejść. Przechwytywacz jest naprawdę potrzebny. Nie jestem pewien, czy ktoś zna lepszy sposób.
Piotr Stulinski,
6
Cześć, w angular4 _defaultOptionsjest chroniony, więc nie można go wezwać ze służby
Andurit
24

Chociaż odpowiadam na to bardzo późno, ale może pomóc komuś innemu. Aby wstawić nagłówki do wszystkich żądań, gdy @NgModulesą używane, można wykonać następujące czynności:

(Przetestowałem to w 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');
    }
}

Teraz @NgModulewykonaj następujące czynności:

@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
źródło
4
potrzebujesz @Injectable i zdefiniuj nagłówki w klasie, przetestowałem pomyślnie przez klasę eksportu @Injectable () CustomRequestOptions rozszerza BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack
cóż, zrobiłem to w 2.0.0, nie sprawdziłem 2.0.1
EasonBlack
Ważna uwaga tutaj natknąłem się na problem, w którym nie można było wstrzyknąć niczego, CustomRequestOptionsnawet przy użyciu @ Inject / @ Injectable. Uświadomiłem sobie, że rozwiązaniem byłoby rozszerzenie RequestOptions, a nie rozszerzenie BaseRequestOptions. Zapewnianie BaseRequestOptionsnie działa, ale RequestOptionszamiast tego rozszerzenie powoduje, że DI działa ponownie.
parlament,
5
To rozwiązanie jest proste, ale jeśli użytkownik wyloguje się i zaloguje ponownie, a jego token się zmieni - nie będzie już działać, ponieważ Authorizationnagłówek jest ustawiany tylko raz podczas inicjowania aplikacji.
Alex Paramonov,
Tak, popraw @AlexeyVParamonov. Jest to przydatne tylko wtedy, gdy token jest ustawiany raz. W przeciwnym razie napiszemy przechwytywacze dla sprawy, jak powiedziałeś.
Shashank Agrawal
15

W Angular 2.1.2I chce tego rozszerzając kątowa 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)
  }
}

potem u moich dostawców aplikacji mogłem użyć niestandardowej fabryki, aby zapewnić „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(){

  }
}

teraz nie muszę deklarować każdej metody HTTP i mogę używać httpnormalnie w całej mojej aplikacji.

jonnie
źródło
Ta odpowiedź działała dla mnie najlepiej, ponieważ mogłem filtrować adres URL do mojego serwera interfejsu API i dodawać tylko token uwierzytelniania do wykonanych połączeń. Zmieniłem żądanie na: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {options = this._setCustomHeaders (opcje); } return super.request (url, options)}
Chris Holcomb
W moim przypadku withCredentials and Headers zostały pobrane z parametru url w metodzie request. Zmieniłem kod w następujący sposób: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {options = this._setCustomHeaders (opcje); if (typeof (url) === "object") {(<Request> url) .withCredentials = options.withCredentials; (<Request> url) .headers = options.headers; } return super.request (url, options)}
Argnist
request()Metoda, którą są przeciążenia, ma dwa podpisy połączeń i optionswłaściwość jest używana tylko wtedy, gdy urlokreślona jako ciąg. W przypadku, gdy urljest to wystąpienie Request, optionswłaściwość jest po prostu ignorowana. Może to prowadzić do trudnych do wykrycia błędów. Proszę zobaczyć moją odpowiedź, aby uzyskać więcej informacji.
Slava Fomin II
Pamiętaj, że to rozwiązanie ma pewne problemy z platformą serwerową . Istnieją jednak obejścia tego problemu .
Alireza Mirian
To działało dla mnie aż do kątowego 4.2. 4.3 Ma przechwytywacze.
cabaji99
12

Utwórz niestandardową klasę HTTP, rozszerzając Httpdostawcę Angular 2 i po prostu przesłoń metodę constructori requestw swojej niestandardowej klasie HTTP. Poniższy przykład dodaje Authorizationnagłówek do każdego żądania 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);
    };
  }
}

Następnie skonfiguruj urządzenie główne, app.module.tsaby podawało XHRBackendjako ConnectionBackenddostawcę i RequestOptionsniestandardową klasę HTTP:

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

Następnie możesz korzystać z niestandardowego dostawcy HTTP w swoich usługach. Na przykład:

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

Oto obszerny przewodnik - http://adonespitogo.com/articles/angular-2-extending-http-provider/

Adones Pitogo
źródło
To podejście dobrze nadaje się do korzystania z alternatywnego dostawcy klasy. Zamiast „dostarcz: HttpService”, tak jak masz to w swoim module, możesz zamiast tego użyć „dostarcz: Http”, pozwalając na pracę z Http w zwykły sposób.
Gilbert Arenas Dagger
Jak mogę dodać dodatkowe właściwości do tej rozszerzonej klasy http? Na przykład router: Router lub dowolne niestandardowe usługi wstrzykiwane.
shafeequemat
@shafeequemat Nie możesz tego zrobić za pomocą tego. Możesz na przykład zdefiniować inną metodę w niestandardowej klasie http setRouter(router). Możesz też utworzyć inną klasę i wprowadzić tam niestandardową klasę HTTP zamiast na odwrót.
Adones Pitogo,
9

W przypadku Angulara 5 i nowszych możemy użyć HttpInterceptor do uogólnienia operacji żądania i odpowiedzi. Pomaga nam to uniknąć powielania:

1) Wspólne nagłówki

2) Określenie typu odpowiedzi

3) Żądanie zapytania

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

Możemy użyć tej klasy AuthHttpInterceptor jako dostawcy dla 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 {
}
Praczi
źródło
8

Lepiej późno niż wcale ... =)

Możesz przyjąć koncepcję rozszerzonego BaseRequestOptions(stąd https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options ) i odświeżyć nagłówki „w locie „(nie tylko w konstruktorze). Można użyć zastąpienia właściwości getter / setter „headers” w następujący sposób:

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 };
Александр Ильинский
źródło
mała aktualizacja: dla lepszej wydajności możesz rozważyć przeniesienie wszystkich statycznych nagłówków (takich jak „Content-Type”) do konstruktora
Александр Ильинский
7

Tak zrobiłem, ustawiając token przy każdym żądaniu.

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

I zarejestruj się w app.module.ts

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule
    ],
    providers: [
        { provide: RequestOptions, useClass: CustomRequestOptions }
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
Rajkeshwar Prasad
źródło
6

Oto ulepszona wersja zaakceptowanej odpowiedzi, zaktualizowana do 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());
    }
}

Oczywiście należy go rozszerzyć na metody takie jak deleteiput razie potrzeby (nie potrzebuję ich jeszcze w tym momencie w moim projekcie).

Zaletą jest to, że w get/post metodach / ... .

Pamiętaj, że w moim przypadku używam plików cookie do uwierzytelnienia. Potrzebowałem nagłówka dla i18n ( Accept-Languagenagłówek), ponieważ wiele wartości zwracanych przez nasz interfejs API zostało przetłumaczonych na język użytkownika. W mojej aplikacji usługa i18n obsługuje język aktualnie wybrany przez użytkownika.

Pierre Henry
źródło
w jaki sposób skłoniłeś tslint do ignorowania nagłówków jako let?
Winnemucca,
5

Co powiesz na utrzymanie oddzielnej usługi, jak poniżej

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

a kiedy dzwonisz do tego z innego miejsca użyj this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

a zobaczysz dodany nagłówek, np .: - Autoryzacja w następujący sposób

wprowadź opis zdjęcia tutaj

co2f2e
źródło
5

Po pewnym dochodzeniu znalazłem ostatni i najłatwiejszy sposób na rozszerzenie, BaseRequestOptionsktóre wolę.
Oto sposoby, z jakich próbowałem i zrezygnowałem z jakiegoś powodu:
1. Rozszerz BaseRequestOptionsi dodaj dynamiczne nagłówki constructor(). To nie może działać, jeśli się zaloguję. Zostanie utworzony raz. Więc to nie jest dynamiczne.
2. przedłużyć Http. Z tego samego powodu, co powyżej, nie mogę dodać dynamicznych nagłówków constructor(). A jeśli przepiszę request(..)metodę i ustawię nagłówki, to tak:

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

Musisz tylko zastąpić tę metodę, ale nie wszystkie metody get / post / put.

3. A moim preferowanym rozwiązaniem jest rozszerzenie BaseRequestOptionsi zastąpienie 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;
 }
}

Ta merge()funkcja zostanie wywołana dla każdego żądania.

Mavlarn
źródło
Spośród wszystkich udzielonych odpowiedzi jest to odpowiedź, na którą zwróciłem uwagę, ponieważ wybrałem już rozwiązanie oparte na rozszerzaniu BaseRequestOptions. Niestety nie zadziałało to dla mnie. jakieś możliwe powody?
vigamage
sprawiło, że działało. to rozwiązanie jest w porządku i miałem problem z moim serwerem. Musiałem zrobić kilka konfiguracji dla żądań CORS przed lotem. skorzystaj z tego linku stackoverflow.com/a/43962690/3892439
vigamage
Jak wiążesz AuthRequestOptionssię z resztą aplikacji? Próbowałem umieścić to w providerssekcji, ale nic nie zrobiłem.
Travis Parks,
Musisz zastąpić dostawcę RequestOptions, nie BaseRequestOptions. angular.io/api/http/BaseRequestOptions
Travis Parks
W mojej aplikacji po prostu rozszerzam BaseRequestOptions i już rozszerza RequestOptions. Następnie w app.module powinieneś ustawić dostawców:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn
5

Chociaż odpowiadam na to bardzo późno, ale jeśli ktoś szuka łatwiejszego rozwiązania.

Możemy użyć angular2-jwt. angular2-jwt przydaje się automatycznie dołączając token JSON Web Token (JWT) jako nagłówek autoryzacji podczas wysyłania żądań HTTP z aplikacji Angular 2.

Możemy ustawić globalne nagłówki z zaawansowaną opcją konfiguracji

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

I wysyłanie tokena na żądanie jak

    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
źródło
przydałaby się goto github.com/auth0/angular2-jwt#installation i dostosowała tę odpowiedź za pomocą przewodnika instalacji
Zuriel,
4

Podoba mi się pomysł zastąpienia domyślnych opcji, wydaje się to dobrym rozwiązaniem.

Jeśli jednak zamierzasz rozszerzyć Httpklasę. Przeczytaj to!

Niektóre odpowiedzi tutaj pokazują nieprawidłowe przeładowanie request()metody, co może prowadzić do trudnych do uchwycenia błędów i dziwnego zachowania. Natknąłem się na to sam.

To rozwiązanie jest oparte na request()implementacji metod w Angular 4.2.x, ale powinno być kompatybilne w przyszłości:

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;

  }

}

Zauważ, że importuję oryginalną klasę w ten sposób import { Http as NgHttp } from '@angular/http';, aby zapobiec konfliktom nazw.

Problem rozwiązany tutaj polega na tym, że request()metoda ma dwie różne sygnatury wywołań. Gdy Requestobiekt jest przekazywany zamiast adresu URL string, optionsargument jest ignorowany przez Angular. Tak więc oba przypadki muszą być odpowiednio obsługiwane.

A oto przykład, jak zarejestrować tę przesłoniętą klasę w kontenerze 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
  );
}

Przy takim podejściu możesz wstrzykiwać Http normalnie klasę, ale zamiast tego twoja przesłonięta klasa zostanie wstrzyknięta magicznie. Pozwala to na łatwą integrację rozwiązania bez zmiany innych części aplikacji (polimorfizm w akcji).

Wystarczy dodać httpProviderdo providerswłaściwości metadanych modułu.

Slava Fomin II
źródło
1

Najprostszy ze wszystkich

Utwórz config.tsplik

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

Następnie servicezaimportuj config.tsplik

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

Myślę, że to było najprostsze i najbezpieczniejsze.

Joshua Fabillar
źródło
0

Wprowadzono pewne zmiany w wersji kątowej 2.0.1 i nowszej:

    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
źródło
Nie działa, sam to wypróbowałem. Nie jest wywoływane tylko z powodu odświeżenia.
Phil
0

Mogę wybrać prostsze rozwiązanie> Dodaj nowe nagłówki do domyślnych opcji scalania lub wczytywania przez funkcję API ap (get) lub inną.

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

Oczywiście możesz uzewnętrznić te Nagłówki w domyślnych opcjach lub cokolwiek w swojej klasie. Znajduje się to w generowanym przez Ionic interfejsie API klasy eksportu api.ts @Injectable () {}

Jest bardzo szybki i działa dla mnie. Nie chciałem formatu json / ld.

Paul Leclerc
źródło
-4

Możesz używać canActivena swoich trasach:

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

Pobrano z: https://auth0.com/blog/angular/angular-2-authentication

W
źródło