Angular: establezca encabezados para cada solicitud

334

Necesito configurar algunos encabezados de autorización después de que el usuario haya iniciado sesión, para cada solicitud posterior.


Para configurar encabezados para una solicitud particular,

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

Referencia

Pero no sería factible configurar manualmente encabezados de solicitud para cada solicitud de esta manera.

¿Cómo configuro los encabezados una vez que el usuario ha iniciado sesión y también elimino esos encabezados al cerrar sesión?

Avijit Gupta
fuente

Respuestas:

379

Para responder, pregunta si podría proporcionar un servicio que envuelva el Httpobjeto original de Angular. Algo como se describe a continuación.

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

Y en lugar de inyectar el Httpobjeto, podría inyectar 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 );
    });
  }
}

También creo que se podría hacer algo usando múltiples proveedores para la Httpclase al proporcionar su propia clase extendiendo la Http... Vea este enlace: http://blog.thoughtram.io/angular2/2015/11/23/multi-providers -in-angular-2.html .

Thierry Templier
fuente
1
donde es 'this.http = http;' proviene de, creo que tenemos que declararlo antes de usar?
co2f2e
1
Encabezados angulares (funciones de establecer y agregar) está "normalizando" la clave del encabezado y la hace minúscula. De Headers.d.ts: // "Los juegos de caracteres HTTP se identifican mediante tokens que no distinguen entre mayúsculas y minúsculas" // Especificaciones en tools.ietf.org/html/rfc2616 Para aquellos que no tienen un backend que funciona según la especificación; aquí hay un bypass: let headersMap = .get (opciones, 'headers._headersMap', new Map ()); headersMap.set ('Autorización', [ .replace ( Bearer ${token}, / \ "/ g, '')]);
sangress
@DiegoUnanue Estoy usando la versión final de Angular 2 y los trabajos de implementación de Thierry. Simplemente reemplace 'angular2' por '@angular' en las declaraciones de importación.
f123
Mark Pieszak: ¿debo incluir proveedores para HttpClient?
user3127109
ahora TS arroja el error: `Argumento de tipo '{encabezados: encabezados; } 'no es asignable al parámetro de tipo' RequestOptionsArgs ''
elporfirio
142

Interceptores HTTP están ahora disponibles a través de la nueva HttpClientde @angular/common/http, a partir de las versiones 4.3.x angular y más allá .

Es bastante simple agregar un encabezado para cada solicitud ahora:

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 un principio de inmutabilidad , esa es la razón por la que la solicitud debe clonarse antes de configurar algo nuevo en ella.

Como editar encabezados es una tarea muy común, en realidad hay un atajo para ello (al clonar la solicitud):

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

Después de crear el interceptor, debe registrarlo usando el HTTP_INTERCEPTORSprovide.

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}
Edmundo Rodrigues
fuente
Implementé esto y al hacer ng serve puedo ver los encabezados de solicitud, sin embargo, al hacer ng b prod e implementar dentro de un tomcat, no veo los encabezados ... usando spring-boot, ¿a dónde fueron los encabezados?
naoru
1
No sé si es porque estoy trabajando con una API de nodo Express, pero no funciona para mí incluso con el documento oficial de Angular. : /
Maxime Lafarie
ERROR TypeError: CreateListFromArrayLike llamado sin objeto
DAG
1
¿Cómo inyectarías algo en HttpInterceptor?
zaitsman
He implementado lo mismo, pero en el encabezado de solicitud PUT y DELETE API no funciona para mí.
Pooja
78

La extensión BaseRequestOptionspodría ser de gran ayuda en este escenario. Mira el siguiente 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 })
]);

Esto debería incluir 'Mi encabezado personalizado' en cada llamada.

Actualizar:

Para poder cambiar el encabezado en cualquier momento que desee en lugar del código anterior, también puede usar el siguiente código para agregar un nuevo encabezado:

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

para eliminar puedes hacer

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

También hay otra función que puede usar para establecer el valor:

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

La solución anterior todavía no es completamente válida en el contexto mecanografiado. _defaultHeaders está protegido y no se debe usar de esta manera. Recomendaría la solución anterior para una solución rápida, pero a largo plazo es mejor escribir su propio contenedor alrededor de las llamadas http que también maneja la autenticación. Tome el siguiente ejemplo de auth0 que es mejor y limpio.

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

Actualización: junio de 2018 , veo que muchas personas optan por esta solución, pero aconsejaría lo contrario. Al agregar un encabezado global, se enviará un token de autenticación a cada llamada a la API que salga de su aplicación. Por lo tanto, las llamadas de la API que se dirigen a complementos de terceros como Intercom o Zendesk o cualquier otra API también llevarán su encabezado de autorización. Esto podría resultar en una gran falla de seguridad. Por lo tanto, use el interceptor globalmente, pero verifique manualmente si la llamada saliente se dirige hacia el punto final de la API de su servidor o no y luego adjunte el encabezado de autenticación.

anit
fuente
1
this.http._defaultOptions.headers.delete ('My-Custom-Header') Por lo tanto, el proceso anterior se puede acortar siguiendo el código this.http._defaultOptions.headers.append ('My-New-Custom-Header', 'newvalue ')
anit
2
@Dinistro sí, ahora no me recomendaré hacer esto. Tuve que encontrar esta solución debido a las limitaciones beta angulares y mi hábito de controlar el flujo de autenticación a nivel mundial. Pero creo que por ahora github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts tiene una solución mejor y limpia.
Anit
1
El problema de usar BaseRequestOptions es que su constructor se ejecuta solo una vez en la vida útil de la aplicación en el navegador. Entonces, si desea cambiar el valor del encabezado durante el tiempo (por ejemplo, csrf_token) no puede hacerlo de esta manera (incluso anular el método de fusión en esta clase no ayuda :()
Kamil Kiełczewski
1
El problema es que si usa un contenedor, las bibliotecas de terceros que acceden a HTTP directamente deben reescribirse para usarlo. Todavía no sé cómo evitar eso. Realmente se necesita un interceptor. No estoy seguro si alguien conoce una mejor manera.
Piotr Stulinski
66
Hola, en angular4 _defaultOptionsestá protegido, así que no se puede llamar desde el servicio
Andurit
24

Aunque estoy respondiendo muy tarde, podría ayudar a alguien más. Para inyectar encabezados a todas las solicitudes cuando @NgModulese usa, se puede hacer lo siguiente:

(Probé esto en 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');
    }
}

Ahora @NgModulehaga lo siguiente:

@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
fuente
44
necesita @Injectable y definir encabezados en clase, probé con éxito mediante la clase de exportación @Injectable () CustomRequestOptions extiende BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack
bueno, hice esto en 2.0.0, no
comprobé
Nota importante aquí Me encontré con un problema en el que era imposible inyectar nada CustomRequestOptionsincluso cuando usaba @ Inject / @ Injectable. La solución que me di cuenta fue extender RequestOptions, no BaseRequestOptions. Proporcionar BaseRequestOptionsno funcionará, pero extenderlo RequestOptionshace que DI vuelva a funcionar.
Parlamento
55
Esta solución es simple, pero si el usuario cierra sesión y vuelve a iniciar sesión y su token cambia, ya no funcionará, porque el Authorizationencabezado se configura solo una vez en el inicio de la aplicación.
Alex Paramonov
Sí, corrige @AlexeyVParamonov. Esto es útil solo si el token se configura una vez. De lo contrario, escribiremos los interceptores para el caso como usted dijo.
Shashank Agrawal
15

En Angular 2.1.2me acerqué a esto extendiendo el 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)
  }
}

luego en mis proveedores de aplicaciones pude usar una fábrica personalizada para proporcionar '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(){

  }
}

ahora no necesito declarar todos los métodos Http y puedo usarlos httpnormalmente en toda mi aplicación.

jonnie
fuente
Esta respuesta funcionó mejor para mí, ya que pude filtrar la url a mi servidor api y solo agregué el token de autenticación a las llamadas realizadas. Cambié la solicitud a: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {opciones = this._setCustomHeaders (opciones); } return super.request (url, options)}
Chris Holcomb
En mi caso, las credenciales y los encabezados se tomaron del parámetro url en el método de solicitud. Cambié código como este: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {options = this._setCustomHeaders (options); if (typeof (url) === "objeto") {(<Request> url) .withCredentials = options.withCredentials; (<Solicitud> url) .headers = options.headers; } return super.request (url, options)}
Argnist
El request()método, que está sobrecargando, tiene dos firmas de llamada y la optionspropiedad se usa solo cuando se urlespecifica como cadena. En caso de que urlsea ​​una instancia de Request, la optionspropiedad simplemente se ignora. Esto podría conducir a errores difíciles de detectar. Por favor vea mi respuesta para más detalles.
Slava Fomin II
Tenga en cuenta que esta solución tiene algunos problemas con la plataforma del servidor . Sin embargo, existen soluciones para evitarlo .
Alireza Mirian
Esto funcionó para mí hasta angular 4.2. 4.3 Tiene interceptores.
cabaji99
12

Cree una clase Http personalizada extendiendo el Httpproveedor Angular 2 y simplemente anule el método constructory requesten su clase Http personalizada. El siguiente ejemplo agrega Authorizationencabezado en cada solicitud 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);
    };
  }
}

Luego configure su main app.module.tspara proporcionar el XHRBackendcomo el ConnectionBackendproveedor y el RequestOptionsa su clase 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 ]
})

Después de eso, ahora puede usar su proveedor http personalizado en sus servicios. Por ejemplo:

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

Aquí hay una guía completa: http://adonespitogo.com/articles/angular-2-extending-http-provider/

Adones Pitogo
fuente
Este enfoque es muy adecuado para usar un proveedor de clase alternativo. En lugar de "proporcionar: HttpService" como lo tiene en su módulo, podría usar "proporcionar: Http" que le permite trabajar con Http como lo haría normalmente.
La daga Gilbert Arenas el
¿Cómo puedo agregar propiedades adicionales a esta clase http extendida? Por ejemplo, enrutador: enrutador o cualquier servicio inyectable personalizado.
shafeequemat
@shafeequemat No puedes hacer eso usando esto. Puede definir otro método en su clase http personalizada, por ejemplo setRouter(router). O puede crear otra clase e inyectar su clase http personalizada allí en lugar de lo contrario.
Adones Pitogo
9

Para Angular 5 y superior, podemos usar HttpInterceptor para generalizar las operaciones de solicitud y respuesta. Esto nos ayuda a evitar duplicar:

1) encabezados comunes

2) Especificando el tipo de respuesta

3) Solicitud 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 clase AuthHttpInterceptor como proveedor de los 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
fuente
8

Más vale tarde que nunca ... =)

Puede tomar el concepto de extendido BaseRequestOptions(desde aquí https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options ) y actualizar los encabezados "sobre la marcha "(no solo en constructor). Puede usar la anulación de la propiedad "encabezados" getter / setter de esta manera:

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 };
Александр Ильинский
fuente
pequeña actualización: para un mejor rendimiento, puede considerar mover todos los encabezados estáticos (como 'Content-Type') al constructor
Александр Ильинский
7

Así es como lo hice para configurar el token con cada solicitud.

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

Y regístrese en app.module.ts

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

Aquí hay una versión mejorada de la respuesta aceptada, actualizada para 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());
    }
}

Por supuesto, debe extenderse para métodos como deletey putsi es necesario (todavía no los necesito en este momento en mi proyecto).

La ventaja es que hay menos código duplicado en los métodos get/ post/ ...

Tenga en cuenta que en mi caso uso cookies para la autenticación. Necesitaba el encabezado para i18n (el Accept-Languageencabezado) porque muchos valores devueltos por nuestra API están traducidos al idioma del usuario. En mi aplicación, el servicio i18n contiene el idioma seleccionado actualmente por el usuario.

Pierre Henry
fuente
¿Cómo conseguiste tslint para ignorar los encabezados como let?
Winnemucca
5

¿Qué tal mantener un servicio separado como sigue?

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

y cuando llamas a esto desde otro lugar usa this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

y verá el encabezado agregado, por ejemplo: - Autorización de la siguiente manera

ingrese la descripción de la imagen aquí

co2f2e
fuente
5

Después de un poco de investigación, encontré la última y la forma más fácil es extender BaseRequestOptionslo que prefiero.
Las siguientes son las formas en que intenté y me di por vencido por alguna razón:
1. extender BaseRequestOptionsy agregar encabezados dinámicos constructor(). No puede funcionar si inicio sesión. Será creado una vez. Entonces no es dinámico.
2. extender Http. La misma razón que la anterior, no puedo agregar encabezados dinámicos constructor(). Y si reescribo el request(..)método y establezco encabezados, así:

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

Solo necesita sobrescribir este método, pero no todos los métodos get / post / put.

3.Y mi solución preferida es extender BaseRequestOptionsy sobrescribir 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;
 }
}

merge()Se llamará a esta función para cada solicitud.

Mavlarn
fuente
Entre todas las respuestas dadas, esta es la respuesta que me llamó la atención ya que ya busqué una solución basada en la extensión BaseRequestOptions. Sin embargo, lamentablemente, esto no funcionó para mí. alguna razón posible?
vigamage
Lo tengo funcionando. esta solución está bien y tuve un problema en mi servidor. Tuve que hacer algunas configuraciones para las solicitudes previas al vuelo de CORS. consulte este enlace stackoverflow.com/a/43962690/3892439
vigamage
¿Cómo se vincula AuthRequestOptionscon el resto de la aplicación? Traté de poner esto en la providerssección pero no hizo nada.
Travis Parks
Debe anular el proveedor para RequestOptions, no BaseRequestOptions. angular.io/api/http/BaseRequestOptions
Travis Parks
En mi aplicación, solo extiendo BaseRequestOptions, y ya extiende RequestOptions. Luego, en app.module, debe configurar los proveedores:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn
5

Aunque estoy respondiendo esto muy tarde, pero si alguien está buscando una solución más fácil.

Podemos usar angular2-jwt. angular2-jwt es útil para adjuntar automáticamente un JSON Web Token (JWT) como encabezado de autorización cuando se realizan solicitudes HTTP desde una aplicación Angular 2.

Podemos establecer encabezados globales con la opción de configuración avanzada

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

Y enviando por solicitud token 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
fuente
sería útil ir a github.com/auth0/angular2-jwt#installation y adaptar esta respuesta usando su guía de instalación
Zuriel
4

Me gusta la idea de anular las opciones predeterminadas, esto parece una buena solución.

Sin embargo, si estás dispuesto a extender la Httpclase. ¡Asegúrate de leer esto!

Algunas respuestas aquí en realidad muestran una sobrecarga incorrecta del request()método, lo que podría conducir a errores difíciles de detectar y un comportamiento extraño. Me he topado con esto yo mismo.

Esta solución se basa en la request()implementación del método en Angular 4.2.x, pero debería ser compatible con el 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;

  }

}

Tenga en cuenta que estoy importando la clase original de esta manera import { Http as NgHttp } from '@angular/http';para evitar conflictos de nombres.

El problema abordado aquí es que el request()método tiene dos firmas de llamada diferentes. Cuando Requestse pasa el objeto en lugar de la URL string, optionsAngular ignora el argumento. Entonces ambos casos deben ser manejados adecuadamente.

Y aquí está el ejemplo de cómo registrar esta clase anulada con el contenedor DI:

export const httpProvider = {
  provide: NgHttp,
  useFactory: httpFactory,
  deps: [XHRBackend, RequestOptions, AuthenticationStateService]
};


export function httpFactory (
  xhrBackend: XHRBackend,
  requestOptions: RequestOptions,
  authenticationStateService: AuthenticationStateService
): Http {
  return new Http(
    xhrBackend,
    requestOptions,
    authenticationStateService
  );
}

Con este enfoque, puede inyectar la Httpclase normalmente, pero su clase anulada se inyectará mágicamente en su lugar. Esto le permite integrar su solución fácilmente sin cambiar otras partes de la aplicación (polimorfismo en acción).

Simplemente agregue httpProvidera la providerspropiedad de los metadatos de su módulo.

Slava Fomin II
fuente
1

El mas simple de todos

Crear un config.tsarchivo

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

Luego en tu service, solo importa el config.tsarchivo

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

Creo que fue el más simple y el más seguro.

Joshua Fabillar
fuente
0

Hubo algunos cambios para angular 2.0.1 y 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
fuente
No funciona, lo intenté yo mismo. No recibe nada más que actualizar.
Phil
0

Puedo elegir una solución más simple> Agregar nuevos encabezados a las opciones predeterminadas fusionar o cargar mediante su función api get (u otra).

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

Por supuesto, puede externalizar estos encabezados en las opciones predeterminadas o lo que sea en su clase. Esto se encuentra en la API de clase de exportación api.ts @Injectable () generada por Ionic {}

Es muy rápido y funciona para mí. No quería el formato json / ld.

Paul Leclerc
fuente
-4

Puedes usar canActiveen tus rutas, así:

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

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

A
fuente