Angular: establezca encabezados para cada solicitud

358

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


Para establecer encabezados para una solicitud en 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 establecer manualmente encabezados de solicitud para cada solicitud de esta manera.

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

1
389

Para responder, tiene la pregunta de que podría proporcionar un servicio que envuelva el Httpobjeto original de Angular. Algo parecido a lo que 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ías 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 proporcionando su propia clase extendiendo la Http... Vea este enlace: http://blog.ilsttram.io/angular2/2015/11/23/multi-providers -in-angular-2.html .

9
  • 1
    donde está 'this.http = http;' proviene, creo que debemos declararlo antes de usarlo?
    co2f2e
    7 oct 2016 a las 2:04
  • 1
    Encabezados angulares (funciones de establecer y añadir) "normaliza" la clave del encabezado y la convierte en minúsculas. De Headers.d.ts: // "Los conjuntos de caracteres HTTP se identifican mediante tokens que no distinguen entre mayúsculas y minúsculas" // Especificación en tools.ietf.org/html/rfc2616 Para aquellos que no tienen un backend que funcione según las especificaciones; aquí hay un desvío: let headersMap = .get (options, 'headers._headersMap', new Map ()); headersMap.set ('Autorización', [ .replace ( Bearer ${token}, / \ "/ g, '')]); 18/10/2016 a las 19:22
  • @DiegoUnanue Estoy usando la versión final de Angular 2 y la implementación de Thierry funciona. Simplemente reemplace 'angular2' por '@angular' en las declaraciones de importación.
    f123
    28 oct 2016 a las 3:57
  • Mark Pieszak: ¿debo incluir proveedores para HttpClient? 21/12/2016 a las 11:16
  • ahora TS arroja un error: 'Argumento de tipo' {encabezados: Encabezados; } 'no se puede asignar al parámetro de tipo' RequestOptionsArgs'` 6 de junio de 2017 a las 15:34
163

Los interceptores HTTP ahora están disponibles a través del nuevo HttpClientdesde @angular/common/http, a partir de las versiones Angular 4.3.xy posteriores .

Ahora es bastante simple agregar un encabezado para cada solicitud:

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.append('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 establecer algo nuevo en ella.

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

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

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

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}
8
  • Implementé esto y cuando hago ng serve puedo ver los encabezados de la solicitud, sin embargo, al hacer ng b prod y desplegarlo dentro de un tomcat, no veo los encabezados ... usando spring-boot, ¿a dónde fueron los encabezados?
    naoru
    15 de agosto de 2017 a las 1:56
  • 1
    No sé si es porque estoy trabajando con una API de nodo Express, pero no me funciona incluso con el documento oficial de Angular. : / 25 de agosto de 2017 a las 7:42
  • ERROR TypeError: CreateListFromArrayLike llamado en un no objeto
    DAG
    25 de agosto de 2017 a las 23:07
  • 1
    ¿Cómo inyectarías algo en HttpInterceptor? 19/10/2017 a las 23:38
  • Implementé estas mismas cosas, pero en PUT y DELETE API el encabezado de solicitud no funciona para mí.
    Pooja
    12 feb 2018 a las 11:19
82

Extender BaseRequestOptionspuede ser de gran ayuda en este escenario. Consulte 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 debe 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 borrar 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 del texto mecanografiado. _defaultHeaders está protegido y no se supone que se use así. 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 más limpio.

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

Actualización: junio de 2018 Veo a mucha gente optando por esta solución, pero aconsejaría lo contrario. Agregar encabezado globalmente enviará un token de autenticación a cada llamada de API que salga de su aplicación. Por lo tanto, las llamadas de 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. Entonces, en su lugar, use el interceptor globalmente pero verifique manualmente si la llamada saliente es hacia el punto final de la API de su servidor o no y luego adjunte el encabezado de autenticación.

11
  • 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
    28 de enero de 2016 a las 3:29
  • 2
    @Dinistro sí, ahora no me recomendaría hacer esto. Tuve que encontrar esta solución debido a las limitaciones angulares de la beta 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 más limpia.
    anit
    21 de marzo de 2016 a las 12:16
  • 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 :() 7 de julio de 2016 a las 8:40
  • 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 de si alguien conoce una forma mejor. 12 de agosto de 2016 a las 16:09
  • 6
    Hola, en angular4 _defaultOptionsestá protegido, por lo que no se puede llamar desde el servicio
    Andurit
    29 de marzo de 2017 a las 12:02
24

Aunque la contesto 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]
})
5
  • 4
    necesitas @Injectable y definir encabezados en la clase, probé exitosamente con @Injectable () la clase de exportación CustomRequestOptions extiende BaseRequestOptions {encabezados: Encabezados = nuevos Encabezados ({'Autorización': 'xxx'}); } 12 oct 2016 a las 7:49
  • bueno, hice esto en 2.0.0, no verifiqué 2.0.1 12/10/2016 a las 7:53
  • Nota importante: me encontré con un problema en el que era imposible inyectar nada CustomRequestOptionsincluso cuando se usaba @ Inject / @ Injectable. La solución que me di cuenta fue extender RequestOptions, no BaseRequestOptions. Proporcionar BaseRequestOptionsno funcionará, pero extender, en RequestOptionscambio, hace que DI vuelva a funcionar. 9/11/2016 a las 14:07
  • 5
    Esta solución es simple, pero si el usuario cierra la sesión y vuelve a iniciarla y su token cambia, ya no funcionará, porque el Authorizationencabezado se establece solo una vez en el inicio de la aplicación. 10/11/2016 a las 10:09
  • Sí, correcto @AlexeyVParamonov. Esto es útil solo si el token se configura una vez. De lo contrario, escribiremos los interceptores para el caso como dijiste. 10/11/2016 a las 16:17
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.

5
  • Esta respuesta funcionó mejor para mí, ya que pude filtrar la URL a mi servidor de API y solo agregar 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)} 31/12/2016 a las 11:40
  • En mi caso, las credenciales y los encabezados se tomaron del parámetro url en el método de solicitud. Cambié el código como este: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {options = this._setCustomHeaders (options); if (typeof (url) === "object") {(<Request> url) .withCredentials = options.withCredentials; (<Request> url) .headers = options.headers; } return super.request (url, options)}
    Argnist
    14 de marzo de 2017 a las 2:33
  • 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 dar lugar a errores difíciles de detectar. Consulte mi respuesta para obtener más detalles. 14/06/17 a las 18:36
  • Tenga en cuenta que esta solución tiene algunos problemas con la plataforma del servidor . Sin embargo, existen soluciones para evitarlo . 30/08/17 a las 21:13
  • Esto funcionó para mí hasta angular 4.2. 4.3 Tiene interceptores. 6 oct 2017 a las 23:49
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 un 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 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/

3
  • Este enfoque es adecuado para utilizar un proveedor de clases alternativo. En lugar de "proporcionar: HttpService" como tiene en su módulo, puede utilizar "proporcionar: Http", lo que le permite trabajar con Http como lo haría normalmente. 22/11/2016 a las 22:05
  • ¿Cómo puedo agregar propiedades adicionales a esta clase http extendida? Por ejemplo, enrutador: enrutador o cualquier servicio inyectable personalizado. 23 de septiembre de 2017 a las 2:59
  • @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. 20 de marzo de 2018 a las 12:06
10

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) Especificar 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 para 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 {
}
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 getter / setter "headers" 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 };
1
  • pequeña actualización: para un mejor rendimiento, puede considerar mover todos los encabezados estáticos (como 'Tipo de contenido') al constructor 15 de mayo de 2017 a las 13:25
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 { }
0
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, debería ampliarse para métodos como deletey putsi es necesario (todavía no los necesito en este punto de mi proyecto).

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

Tenga en cuenta que en mi caso utilizo 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.

1
  • ¿Cómo lograste que tslint ignorara los encabezados como lo hiciste? 13 de marzo de 2017 a las 6:55
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 llames 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í

5

Después de investigar un poco, encontré la forma final y la más fácil es extender la BaseRequestOptionsque prefiero.
Las siguientes son las formas en que intenté y renuncié por alguna razón:
1. extender BaseRequestOptionsy agregar encabezados dinámicos constructor(). No puede funcionar si inicio sesión. Se creará una vez. Entonces no es dinámico.
2. extender Http. La misma razón que la anterior, no puedo agregar encabezados dinámicos en 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;
 }
}

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

5
  • Entre todas las respuestas dadas, esta es la respuesta que me llamó la atención ya que ya he optado por una solución que se basa en extender BaseRequestOptions. Sin embargo, lamentablemente, esto no funcionó para mí. alguna posible razón? 13 de mayo de 2017 a las 12:54
  • 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 15 de mayo de 2017 a las 3:51
  • ¿Cómo te relacionas AuthRequestOptionscon el resto de la aplicación? Intenté poner esto en la providerssección pero no hizo nada. 19/06/2017 a las 18:55
  • Debe anular el proveedor por RequestOptions, no BaseRequestOptions. angular.io/api/http/BaseRequestOptions 19 de junio de 2017 a las 19:03
  • En mi aplicación, solo extiendo BaseRequestOptions, y ya extiende RequestOptions. Luego, en app.module, debes configurar los proveedores:{ provide: RequestOptions, useClass: AuthRequestOptions }
    Mavlarn
    20 de junio de 2017 a las 7:00
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 adjuntando automáticamente un JSON Web Token (JWT) como un encabezado de autorización al realizar 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')
    );
}
1
4

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

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

Algunas respuestas aquí muestran en realidad una sobrecarga incorrecta del request()método, lo que podría provocar 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.

The problem addressed here is that request() method has two different call signatures. When Request object is passed instead of the URL string, the options argument is ignored by Angular. So both cases must be properly handled.

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 tal enfoque, puede inyectar 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.

1

El mas simple de todos

Crea 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 sencillo y seguro.

1

Puede crear su propio cliente http con algún encabezado de autorización:

import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class HttpClientWithAuthorization {

  constructor(private http: HttpClient) {}

createAuthorizationHeader(bearerToken: string): HttpHeaders {
  const headerDict = {
    Authorization: 'Bearer ' + bearerToken,
  }
  return new HttpHeaders(headerDict);
}

get<T>(url, bearerToken) {
  this.createAuthorizationHeader(bearerToken);
  return this.http.get<T>(url, {
    headers: this.createAuthorizationHeader(bearerToken)
  });
}

post<T>(url, bearerToken, data) {
  this.createAuthorizationHeader(bearerToken);
  return this.http.post<T>(url, data, {
    headers: this.createAuthorizationHeader(bearerToken)
  });
}
}

Y luego inyéctelo en lugar de HttpClienten su clase de servicio:

@Injectable({
  providedIn: 'root'
})
export class SomeService {

  constructor(readonly httpClientWithAuthorization: HttpClientWithAuthorization) {}

  getSomething(): Observable<Object> {
    return this.httpClientWithAuthorization.get<Object>(url,'someBearer');
  }

  postSomething(data) {
    return this.httpClientWithAuthorization.post<Object>(url,'someBearer', data);
  }
}
1

En lugar de establecer encabezados una y otra vez para cada solicitud, puede usar un interceptor.

Cada solicitud que salga pasará por el interceptor donde puede configurar sus encabezados de autenticación y luego liberar la solicitud.

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 { }
1
  • No funciona, lo probé yo mismo. No se le pide nada más que actualizar.
    Phil
    9 de mayo de 2017 a las 5:04
0

Pude elegir una solución más simple> Agregar nuevos encabezados a las opciones predeterminadas fusionar o cargar por 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 está en la API de clase de exportación api.ts @Injectable () generada por Ionic {}

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

0

Puede utilizar el concepto de interceptor para lograr esto

A continuación se muestra el fragmento de código, el método de intercepción a continuación interceptará cada solicitud y adjuntará el apptoken a los encabezados HTTP

export class MyappintercepInterceptor implements HttpInterceptor {

  constructor() {}

  intercept(request: HttpRequest, next: HttpHandler): Observable> {

    request = request.clone({
      setHeaders: {
        'apptoken': 'SYSTEM'
      }
    });
    
    return next.handle(request);
  }
}

Para obtener más detalles, consulte la siguiente URL.

https://beginnersbug.com/interceptor-in-angular/

-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