Angular - Defina cabeçalhos para cada solicitação

334

Preciso definir alguns cabeçalhos de autorização após o login do usuário, para cada solicitação subseqüente.


Para definir cabeçalhos para uma solicitação específica,

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

Referência

Mas não seria viável definir manualmente cabeçalhos de solicitação para cada solicitação dessa maneira.

Como definir os cabeçalhos depois que o usuário efetuar login e também removê-los no logout?

Avijit Gupta
fonte

Respostas:

378

Para responder, você pergunta que poderia fornecer um serviço que agrupa o Httpobjeto original do Angular. Algo como descrito abaixo.

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

@Injectable()
export class HttpClient {

  constructor(private http: Http) {}

  createAuthorizationHeader(headers: Headers) {
    headers.append('Authorization', 'Basic ' +
      btoa('username:password')); 
  }

  get(url) {
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.get(url, {
      headers: headers
    });
  }

  post(url, data) {
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.post(url, data, {
      headers: headers
    });
  }
}

E em vez de injetar o Httpobjeto, você pode injetar este ( 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 );
    });
  }
}

Eu também acho que algo poderia ser feito usando vários provedores para a Httpclasse, fornecendo sua própria classe estendendo a Http... Consulte este link: http://blog.thoughtram.io/angular2/2015/11/23/multi-providers -em-angular-2.html .

Thierry Templier
fonte
11
onde está 'this.http = http;' vem, acredito que precisamos declarar antes de usar?
co2f2e
11
Os cabeçalhos angulares (funções de definir e acrescentar) estão "normalizando" a tecla do cabeçalho e a tornam minúscula. De Headers.d.ts: // "conjuntos de caracteres HTTP são identificados por tokens que não diferenciam maiúsculas de minúsculas" // Especificação em tools.ietf.org/html/rfc2616 Para aqueles que não possuem um back-end que funcione conforme a especificação; aqui está um desvio: deixe headersMap = .get (opções, 'headers._headersMap', novo Map ()); headersMap.set ('Authorization', [ .replace ( Bearer ${token}, / \ "/ g, '')]);
sangress
@DiegoUnanue Estou usando a versão final dos trabalhos de implementação de Angular 2 e Thierry. Apenas substitua 'angular2' por '@angular' nas instruções de importação.
F123 /
Mark Pieszak - devo incluir provedores para HttpClient?
usar o seguinte comando
agora TS lança erro: 'Argumento do tipo' {headers: Headers; } 'não é atribuível ao parâmetro do tipo' RequestOptionsArgs ''
elporfirio 6/06/17
142

Os interceptores HTTP agora estão disponíveis através do novo HttpClientfrom @angular/common/http, a partir das versões Angular 4.3.x e além .

É muito simples adicionar um cabeçalho para cada solicitação agora:

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

Existe um princípio de imutabilidade , é por isso que a solicitação precisa ser clonada antes de definir algo novo.

Como editar cabeçalhos é uma tarefa muito comum, na verdade há um atalho para ele (ao clonar a solicitação):

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

Depois de criar o interceptador, você deve registrá-lo usando o HTTP_INTERCEPTORSfornecimento.

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}
Edmundo Rodrigues
fonte
Eu implementei isso e, ao fazer o serviço de ng, posso ver os cabeçalhos das solicitações, no entanto, ao fazer ng b prod e implantá-lo dentro de um tomcat, não vejo os cabeçalhos ... usando boot de mola, para onde foram os cabeçalhos?
Naoru
11
Não sei se é porque estou trabalhando com uma API do nó Express, mas ela não funciona para mim, mesmo com o documento oficial do Angular. : /
Maxime Lafarie
ERRO TypeError: CreateListFromArrayLike chamado não-objeto
DAG
11
Como você injetaria algo no HttpInterceptor?
zaitsman
Implementamos as mesmas coisas, mas no cabeçalho de solicitação PUT e DELETE API não funciona para mim.
Pooja
78

A extensão BaseRequestOptionspode ser de grande ajuda nesse cenário. Confira o seguinte código:

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

Isso deve incluir 'Meu cabeçalho personalizado' em todas as chamadas.

Atualizar:

Para poder alterar o cabeçalho a qualquer momento, em vez do código acima, você também pode usar o código a seguir para adicionar um novo cabeçalho:

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

para excluir você pode fazer

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

Também há outra função que você pode usar para definir o valor:

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

A solução acima ainda não é completamente válida no contexto datilografado. _defaultHeaders está protegido e não deve ser usado assim. Eu recomendaria a solução acima para uma solução rápida, mas, a longo prazo, é melhor escrever seu próprio wrapper em torno de chamadas http, que também lida com auth. Veja o exemplo a seguir de auth0, que é melhor e limpo.

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

Atualização - junho de 2018 Vejo muitas pessoas optando por esta solução, mas aconselho o contrário. Anexar cabeçalho globalmente enviará o token de autenticação para todas as chamadas da API que saem do seu aplicativo. Portanto, as chamadas da API que acessam plugins de terceiros, como interfone ou zendesk ou qualquer outra API, também carregam seu cabeçalho de autorização. Isso pode resultar em uma grande falha de segurança. Então, em vez disso, use o interceptador globalmente, mas verifique manualmente se a chamada de saída está direcionada para o terminal da API do servidor ou não e, em seguida, anexe o cabeçalho de autenticação.

anit
fonte
11
this.http._defaultOptions.headers.delete ('My-Custom-Header') Para que o processo acima possa ser reduzido, siga o código this.http._defaultOptions.headers.append ('My-New-Custom-Header', 'newvalue ')
anit 28/01
2
@ Dinistro sim, agora eu não vou me recomendar fazendo isso. Eu tive que criar essa solução alternativa devido às limitações angulares do beta e ao meu hábito de controlar o fluxo de autenticação globalmente. Mas acredito que, por enquanto, o github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts tem uma solução melhor e limpa.
anit
11
O problema de usar BaseRequestOptions é que seu construtor é executado apenas uma vez na vida útil do aplicativo no navegador. Então, se você quiser alterar o valor de cabeçalho durante o tempo (por exemplo csrf_token) você não pode fazê-lo desta forma (mesmo substituindo método merge nesta classe não ajuda :()
Kamil Kiełczewski
11
O problema é que, se você usar uma biblioteca de terceiros de wrapper que acesse HTTP diretamente, precisará ser reescrita para usá-lo. Eu ainda não sei como contornar isso. Um interceptador é realmente necessário. Não tenho certeza se alguém sabe uma maneira melhor.
Piotr Stulinski 12/08
6
Oi, em angular4 _defaultOptionsestá protegido de forma não pode ser chamado de serviço
Andurit
24

Embora eu esteja respondendo muito tarde, isso pode ajudar outra pessoa. Para injetar cabeçalhos em todas as solicitações quando@NgModule usado, pode-se fazer o seguinte:

(Eu testei isso no 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');
    }
}

Agora, @NgModulefaça o seguinte:

@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
fonte
4
você precisa de @Injectable e define cabeçalhos na classe, testei com êxito pela classe de exportação @Injectable () CustomRequestOptions estende BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack 12/10
Bem, eu fiz isso em 2.0.0, não verificar 2.0.1
EasonBlack
Nota importante aqui Corri para um problema em que era impossível injetar qualquer coisa, CustomRequestOptionsmesmo ao usar @ Inject / @ Injectable. A solução que percebi foi estender RequestOptions, não BaseRequestOptions. Fornecer BaseRequestOptionsnão funcionará, mas estender fará com RequestOptionsque o DI funcione novamente.
parlamentar
5
Essa solução é simples, mas se o usuário efetuar logoff e logon novamente e seu token for alterado - não funcionará mais, pois o Authorizationcabeçalho é definido apenas uma vez no init do aplicativo.
Alex Paramonov
Sim, corrija @AlexeyVParamonov. Isso é útil apenas se o token estiver sendo definido uma vez. Caso contrário, escreveremos os interceptadores para o caso, como você disse.
precisa
15

Em Angular 2.1.2eu aproximei isso estendendo o Http angular:

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

então, em meus fornecedores de aplicativos, pude usar uma fábrica personalizada para fornecer '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(){

  }
}

agora não preciso declarar todos os métodos Http e posso usá- httplos normalmente em todo o meu aplicativo.

Jonnie
fonte
Essa resposta funcionou melhor para mim, pois eu era capaz de filtrar o URL no meu servidor api e adicionar apenas o token de autenticação às chamadas feitas a ele. Alterei a solicitação para: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {options = this._setCustomHeaders (opções); } retornar super.request (url, opções)}
Chris Holcomb
No meu caso, withCredentials and Headers foram retirados do parâmetro url no método de solicitação. Alterei o código assim: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {options = this._setCustomHeaders (opções); if (typeof (url) === "objeto") {(<Request> url) .withCredentials = options.withCredentials; (<Request> url) .headers = options.headers; } retornar super.request (url, opções)}
Argnist 14/03
O request()método, que você está sobrecarregando, possui duas assinaturas de chamada e a optionspropriedade é usada apenas quando urlespecificada como string. Caso urlseja uma instância de Request, a optionspropriedade será ignorada. Isso pode levar a erros difíceis de detectar. Por favor, veja minha resposta para mais detalhes.
Slava Fomin II
Observe que esta solução tem alguns problemas com a plataforma do servidor . No entanto, existem soluções alternativas para evitá-lo .
Alireza Mirian
Isso funcionou para mim até o angular 4.2. 4.3 Tem interceptores.
cabaji99
12

Crie uma classe Http personalizada estendendo o Httpprovedor Angular 2 e simplesmente substitua o método constructore requestna sua classe Http personalizada. O exemplo abaixo adiciona Authorizationcabeçalho em cada solicitação 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);
    };
  }
}

Em seguida, configure seu main app.module.tspara fornecer XHRBackendcomo o ConnectionBackendprovedor e a RequestOptionssua classe Http personalizada:

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

Depois disso, agora você pode usar seu provedor http personalizado em seus serviços. Por exemplo:

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

Aqui está um guia completo - http://adonespitogo.com/articles/angular-2-extending-http-provider/

Adones Pitogo
fonte
Essa abordagem é adequada para o uso de um provedor de classe alternativo. Em vez de "fornecer: HttpService" como você possui em seu módulo, você pode usar "fornecer: Http", permitindo que você trabalhe com Http como faria normalmente.
The Gilbert Arenas Dagger
Como posso adicionar propriedades adicionais a essa classe http estendida? Por exemplo, roteador: roteador ou qualquer serviço injetável personalizado.
shafeequemat 23/09/17
@ shafeequemat Você não pode fazer isso usando isso. Você pode definir outro método na sua classe http personalizada, por exemplo setRouter(router). Ou você pode criar outra classe e injetar sua classe http personalizada lá em vez do contrário.
Adones Pitogo 20/03/2019
9

Para o Angular 5 e superior, podemos usar o HttpInterceptor para generalizar as operações de solicitação e resposta. Isso nos ajuda a evitar duplicar:

1) Cabeçalhos comuns

2) Especificando o tipo de resposta

3) Solicitação de consulta

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

Podemos usar esta classe AuthHttpInterceptor como um provedor para os 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
fonte
8

Antes tarde do que nunca ... =)

Você pode adotar o conceito de extended BaseRequestOptions(a partir daqui https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options ) e atualizar os cabeçalhos "on the fly "(não apenas no construtor). Você pode usar a substituição de propriedade "cabeçalhos" getter / setter da seguinte maneira:

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 };
Александр Ильинский
fonte
pequena atualização: para um melhor desempenho, você pode considerar mover todos os cabeçalhos estáticos (como 'Tipo de Conteúdo') para o construtor
Александр Ильинский
7

Foi assim que eu fiz para definir o token com cada solicitação.

import { RequestOptions, BaseRequestOptions, RequestOptionsArgs } from '@angular/http';

export class CustomRequestOptions extends BaseRequestOptions {

    constructor() {
        super();
        this.headers.set('Content-Type', 'application/json');
    }
    merge(options?: RequestOptionsArgs): RequestOptions {
        const token = localStorage.getItem('token');
        const newOptions = super.merge(options);
        if (token) {
            newOptions.headers.set('Authorization', `Bearer ${token}`);
        }

        return newOptions;
    }
}

E registre-se em app.module.ts

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

Aqui está uma versão aprimorada da resposta aceita, atualizada para a final do Angular2:

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

É claro que deve ser estendido para métodos como deletee, putse necessário (ainda não os necessito neste momento do meu projeto).

A vantagem é que há menos código duplicado nos métodos get/ post/ ....

Observe que, no meu caso, eu uso cookies para autenticação. Eu precisava do cabeçalho para i18n (o Accept-Languagecabeçalho) porque muitos valores retornados por nossa API são traduzidos no idioma do usuário. No meu aplicativo, o serviço i18n contém o idioma atualmente selecionado pelo usuário.

Pierre Henry
fonte
como você conseguiu tslint para ignorar os cabeçalhos como let?
Winnemucca
5

Que tal Manter um Serviço Separado, como segue

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


            @Injectable()
            export class HttpClientService extends RequestOptions {

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

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

e quando você chama isso de outro lugar, use this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

e você verá o cabeçalho adicionado, por exemplo: - Autorização da seguinte forma

insira a descrição da imagem aqui

co2f2e
fonte
5

Após alguma investigação, achei a maneira final e mais fácil de estender a BaseRequestOptionsque prefiro.
A seguir, apresentamos as maneiras pelas quais tentei desistir por algum motivo:
1. estenda BaseRequestOptionse inclua cabeçalhos dinâmicos constructor(). Não pode funcionar se eu entrar. Será criado uma vez. Portanto, não é dinâmico.
2. estender Http. Mesmo motivo acima, não consigo adicionar cabeçalhos dinâmicos constructor(). E se eu reescrever o request(..)método e definir cabeçalhos, assim:

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

Você só precisa sobrescrever esse método, mas nem todos os métodos get / post / put.

3.E minha solução preferida é estender BaseRequestOptionse substituir 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;
 }
}

essa merge()função será chamada para cada solicitação.

Mavlarn
fonte
Entre todas as respostas dadas, essa é a resposta que chamou minha atenção, pois eu já procurei uma solução que se baseia na extensão BaseRequestOptions. No entanto, infelizmente, isso não funcionou para mim. alguma razão possível?
Vigamage
conseguiu funcionar. esta solução está boa e tive um problema no meu servidor. Eu tive que fazer algumas configurações para solicitações pré-vôo do CORS. consulte este link stackoverflow.com/a/43962690/3892439
vigamage 15/17
Como você se vincula AuthRequestOptionsao resto do aplicativo? Tentei colocar isso na providersseção, mas não fez nada.
Travis Parks
Você deve substituir o provedor por RequestOptions, não BaseRequestOptions. angular.io/api/http/BaseRequestOptions
Travis Parks
No meu aplicativo, apenas estendo BaseRequestOptions e ele já estende RequestOptions. Em app.module, você deve definir os provedores:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn
5

Embora eu esteja respondendo isso muito tarde, mas se alguém estiver procurando uma solução mais fácil.

Podemos usar angular2-jwt. angular2-jwt é útil anexar automaticamente um JSON Web Token (JWT) como um cabeçalho de autorização ao fazer solicitações HTTP de um aplicativo Angular 2.

Podemos definir cabeçalhos globais com opção de configuração avançada

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

E enviando por token de solicitação como

    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
fonte
Seria útil para Goto github.com/auth0/angular2-jwt#installation e adaptar esta resposta usando o seu guia de instalação
Zuriel
4

Eu gosto da ideia de substituir as opções padrão, essa parece ser uma boa solução.

No entanto, se você estiver disposto a estender o Http classe. Não deixe de ler isso!

Algumas respostas aqui estão realmente mostrando sobrecarga incorreta de request() método, o que pode levar a erros difíceis de capturar e comportamento estranho. Eu já me deparei com isso.

Esta solução é baseada na request()implementação do método em Angular 4.2.x, mas deve ser compatível com o futuro:

import {Observable} from 'rxjs/Observable';
import {Injectable} from '@angular/core';

import {
  ConnectionBackend, Headers,
  Http as NgHttp,
  Request,
  RequestOptions,
  RequestOptionsArgs,
  Response,
  XHRBackend
} from '@angular/http';


import {AuthenticationStateService} from '../authentication/authentication-state.service';


@Injectable()
export class Http extends NgHttp {

  constructor (
    backend: ConnectionBackend,
    defaultOptions: RequestOptions,
    private authenticationStateService: AuthenticationStateService
  ) {
    super(backend, defaultOptions);
  }


  request (url: string | Request, options?: RequestOptionsArgs): Observable<Response> {

    if ('string' === typeof url) {

      url = this.rewriteUrl(url);
      options = (options || new RequestOptions());
      options.headers = this.updateHeaders(options.headers);

      return super.request(url, options);

    } else if (url instanceof Request) {

      const request = url;
      request.url = this.rewriteUrl(request.url);
      request.headers = this.updateHeaders(request.headers);

      return super.request(request);

    } else {
      throw new Error('First argument must be a url string or Request instance');
    }

  }


  private rewriteUrl (url: string) {
    return environment.backendBaseUrl + url;
  }

  private updateHeaders (headers?: Headers) {

    headers = headers || new Headers();

    // Authenticating the request.
    if (this.authenticationStateService.isAuthenticated() && !headers.has('Authorization')) {
      headers.append('Authorization', 'Bearer ' + this.authenticationStateService.getToken());
    }

    return headers;

  }

}

Observe que estou importando a classe original dessa maneira import { Http as NgHttp } from '@angular/http'; para evitar conflitos de nome.

O problema abordado aqui é que o request()método possui duas assinaturas de chamada diferentes. Quando o Requestobjeto é passado em vez da URL string, o optionsargumento é ignorado pelo Angular. Portanto, os dois casos devem ser tratados adequadamente.

E aqui está o exemplo de como registrar essa classe substituída no contêiner 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
  );
}

Com essa abordagem, você pode injetar Httpclasse normalmente, mas sua classe substituída será injetada magicamente. Isso permite que você integre sua solução facilmente, sem alterar outras partes do aplicativo (polimorfismo em ação).

Basta adicionar httpProviderà providerspropriedade dos metadados do seu módulo.

Slava Fomin II
fonte
1

O mais simples de todos

Crie um config.tsarquivo

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

Em seguida, no seu service, basta importar o config.tsarquivo

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

Eu acho que foi o mais simples e o mais seguro.

Joshua Fabillar
fonte
0

Houve algumas alterações no angular 2.0.1 e superior:

    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
fonte
Não funciona, tentei eu mesmo. Não recebe nada além de atualização.
Phil
0

Consegui escolher uma solução mais simples> Adicionar novos cabeçalhos às opções padrão mesclar ou carregar por sua função api get (ou outra).

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

Claro que você pode externalizar esses cabeçalhos nas opções padrão ou em qualquer outra classe da sua classe. Isso está na API de classe de exportação api.ts @Injectable () gerada pelo Ionic {}

É muito rápido e funciona para mim. Eu não queria o formato json / ld.

Paul Leclerc
fonte
-4

Você pode usar canActiveem suas rotas, assim:

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

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

AT
fonte