Кутовий - встановлення заголовків для кожного запиту


334

Мені потрібно встановити деякі заголовки авторизації після входу користувача для кожного наступного запиту.


Щоб встановити заголовки для конкретного запиту,

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

Довідково

Але було б неможливо вручну встановити заголовки запитів для кожного запиту таким чином.

Як встановити встановлені заголовки після входу користувача, а також видалити ці заголовки під час виходу?



Відповіді:


379

Щоб відповісти, ви ставите запитання, чи можете ви надати послугу, яка обгортає оригінальний Httpоб'єкт від Angular. Щось подібне до описаного нижче.

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

І замість того, щоб Httpвводити об'єкт, ви можете ввести цей ( 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 );
    });
  }
}

Я також думаю, що щось можна зробити за допомогою декількох провайдерів для Httpкласу, надавши власний клас, що розширює клас Http... Дивіться це посилання: http://blog.thoughtram.io/angular2/2015/11/23/multi-providers -в-кутовий-2.html .


1
де 'this.http = http;' походить, я вважаю, що нам потрібно декларувати це перед використанням?
co2f2e

1
кутові заголовки (встановлення та додавання функцій) "нормалізує" ключ заголовка і робить його малі. З Headers.d.ts: // "Набори символів HTTP ідентифікуються нечутливими до регістрових маркерів" // Spec на tools.ietf.org/html/rfc2616 Для тих, хто не має бекенда, який працює за специфікацією; ось обхід: нехай headersMap = .get (параметри, 'headers._headersMap', нова карта ()); headersMap.set ('Авторизація', [ .replace ( Bearer ${token}, / \ "/ g, '')])
sangress

@DiegoUnanue Я використовую остаточну версію Angular 2 і реалізація Thierry. Просто замініть 'angular2' на '@angular' у операторах імпорту.
f123

Марк Пієзак - чи варто включати провайдерів для HttpClient?
user3127109

тепер TS видає помилку: `Аргумент типу '{headers: Headers; } 'не призначається параметру типу' RequestOptionsArgs ''
elporfirio

142

HTTP перехоплювачі тепер доступні через новий HttpClientз @angular/common/http, в кутових версіях 4.3.x і за її межами .

Додавати заголовок для кожного запиту зараз досить просто:

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

Існує принцип незмінності , ось чому запит потрібно клонувати, перш ніж встановлювати щось нове.

Оскільки редагування заголовків є дуже поширеною задачею, насправді існує ярлик до нього (під час клонування запиту):

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

Створивши перехоплювач, слід зареєструвати його за допомогою HTTP_INTERCEPTORSпровайдера.

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

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AddHeaderInterceptor,
    multi: true,
  }],
})
export class AppModule {}

Я реалізував це і, виконуючи службу ng, я можу бачити заголовки запитів, проте, роблячи ng b prod та розгортаючи всередині tomcat, я не бачу заголовків ... за допомогою весняного завантаження, куди пішли заголовки?
naoru

1
Не знаю, чи це тому, що я працюю з API вузла Express, але це не працює для мене навіть з офіційним документом Angular. : /
Максим Лафарі

ПОМИЛКА TypeError: CreateListFromArrayLike закликав не об’єкт
DAG

1
Як би ви ввели щось у HttpInterceptor?
zaitsman

Я реалізував ті самі речі, але в заголовку запиту PUT і DELETE API не працює для мене.
Pooja

78

Розширення BaseRequestOptionsможе бути корисною у цьому сценарії. Перевірте наступний код:

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

Це повинно включати "Мій спеціальний заголовок" у кожному дзвінку.

Оновлення:

Щоб мати змогу змінити заголовок у будь-який час замість вищевказаного коду, ви також можете використати наступний код, щоб додати новий заголовок:

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

для видалення ви можете зробити

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

Також є ще одна функція, яку ви можете використовувати для встановлення значення:

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

Наведене вище рішення все ще не є повністю дійсним у контексті typecript. _defaultHeaders захищений і не повинен використовуватися так. Я б рекомендував вищезазначене рішення для швидкого виправлення, але для довгого запуску краще написати власну обгортку навколо http-дзвінків, яка також обробляє auth. Візьміть наступний приклад з auth0, який є кращим і чистішим.

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

Оновлення - червень 2018 року. Я бачу, що багато людей ідуть на рішення, але я б порадив інакше. Примітка заголовка в усьому світі надсилатиме маркер автентифікації на кожен дзвінок api, що виходить із вашого додатка. Таким чином, виклики api, що надходять на сторонні плагіни, такі як домофон або zendesk або будь-який інший api, також матимуть ваш заголовок авторизації. Це може призвести до великих вад безпеки. Отже, використовуйте перехоплювач у всьому світі, але перевіряйте вручну, чи вихідний дзвінок спрямований до кінцевої точки api вашого сервера чи ні, а потім додайте заголовок auth.


1
this.http._defaultOptions.headers.delete ("Мій спеціальний заголовок") Отже, вищезазначений процес можна скоротити за допомогою наступного коду this.http._defaultOptions.headers.append ("Мій-новий-користувальницький заголовок", "newvalue" ')
аніт

2
@Dinistro так, зараз я не рекомендую робити це. Мені довелося придумати таке рішення через кутові обмеження бета-версії та мою звичку контролювати аутентичний потік у глобальному масштабі. Але я вважаю, що на даний момент github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts має кращі та чистіші рішення.
аніт

1
Проблема використання BaseRequestOptions полягає в тому, що його конструктор запускається лише один раз протягом життя програми в браузері. Тож якщо ви хочете змінити значення заголовка протягом часу (наприклад, csrf_token), ви не можете зробити це таким чином (навіть переважаючий метод злиття в цьому класі не допомагає :()
Kamil Kiełczewski

1
Проблема полягає в тому, що якщо ви використовуєте обгорткові сторонні бібліотеки, які безпосередньо отримують доступ до HTTP, для їх використання потрібно переписати їх. Я досі не знаю, як це обійти. Перехоплювач дійсно потрібен. Не впевнений, чи знає хто кращий спосіб.
Пьотр Стулінський

6
Привіт, в angular4 _defaultOptionsзахищено, тому його не можна викликати зі служби
Andurit

24

Хоча я відповідаю на це дуже пізно, але це може допомогти комусь іншому. Для введення заголовків до всіх запитів, коли @NgModuleвони використовуються, можна зробити наступне:

(Я тестував це в кутовій 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');
    }
}

Тепер @NgModuleвиконайте наступне:

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

4
вам потрібні @Injectable та визначити заголовки в класі, я успішно перевірив експортний клас @Injectable (). CustomRequestOptions розширює BaseRequestOptions {заголовки: заголовки = нові заголовки ({'Авторизація': 'xxx'}); }
EasonBlack

добре, я зробив це в 2.0.0, не перевірив 2.0.1
EasonBlack

Важлива примітка. Тут я зіткнувся з проблемою, коли неможливо було нічого ввести CustomRequestOptionsнавіть під час використання @ Inject / @ Injectable. Рішення, яке я зрозумів, було продовжити RequestOptions, а не BaseRequestOptions. Надання BaseRequestOptionsне буде працювати, але продовження RequestOptionsнатомість змушує DI працювати знову.
парламент

5
Це рішення просте, але якщо користувач вийде з системи та ввійде назад, а його маркер зміниться - він більше не працюватиме, оскільки Authorizationзаголовок встановлюється лише один раз на init програми.
Олексій Парамонов

Так, правильно @AlexeyVParamonov Це корисно лише в тому випадку, якщо маркер встановлюється один раз. Інакше ми запишемо перехоплювачі для випадку, як ви сказали.
Shashank Agrawal

15

В Angular 2.1.2Я підійшов до цього за рахунок розширення кутовий Http:

import {Injectable} from "@angular/core";
import {Http, Headers, RequestOptionsArgs, Request, Response, ConnectionBackend, RequestOptions} from "@angular/http";
import {Observable} from 'rxjs/Observable';

@Injectable()
export class HttpClient extends Http {

  constructor(protected _backend: ConnectionBackend, protected _defaultOptions: RequestOptions) {

    super(_backend, _defaultOptions);
  }

  _setCustomHeaders(options?: RequestOptionsArgs):RequestOptionsArgs{
    if(!options) {
      options = new RequestOptions({});
    }
    if(localStorage.getItem("id_token")) {

      if (!options.headers) {

        options.headers = new Headers();


      }
      options.headers.set("Authorization", localStorage.getItem("id_token"))
    }
    return options;
  }


  request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
    options = this._setCustomHeaders(options);
    return super.request(url, options)
  }
}

потім у своїх постачальників додатків я міг використовувати спеціальну фабрику, щоб надати "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(){

  }
}

тепер мені не потрібно декларувати кожен метод Http і я можу використовувати httpяк звичайне протягом моєї програми.


Ця відповідь найкраще працювала для мене, оскільки я зміг відфільтрувати URL на моєму сервісі api та додати лише Auth Token на дзвінки, зроблені на нього. Я змінив запит на: request (url: string | Запит, параметри ?: RequestOptionsArgs): спостерігається <Response> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {options = this._setCustomHeaders (options); } повернути super.request (URL, параметри)}
Кріс Холкомб

У моєму випадку withCredentials та Headers були взяті з параметра url у методі запиту. Я змінив код таким чином: request (url: string | Запит, параметри ?: RequestOptionsArgs): спостерігається <Response> {options = this._setCustomHeaders (параметри); if (typeof (url) === "об'єкт") {(<Request> url) .withCredentials = options.withCredentials; (<Запит> URL) .headers = options.headers; } повернути super.request (URL, параметри)}
Арніст

request()Метод, який ви перевантаження, має два підписи виклику і optionsвластивість використовується тільки тоді , коли urlвказано в вигляді рядка. У випадку, коли urlце екземпляр Request, optionsвластивість просто ігнорується. Це може призвести до важких помилок. Будь ласка, дивіться мою відповідь для отримання більш детальної інформації.
Слава Фомін II


Це працювало для мене до кутового 4.2. 4.3 Має перехоплювачі.
cabaji99

12

Створіть спеціальний клас Http, розширивши Httpпровайдера Angular 2 та просто замініть constructorта requestметод у вашому користувальницькому класі Http. Наведений нижче приклад додає Authorizationзаголовок у кожен запит 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);
    };
  }
}

Потім налаштуйте свій основний так, app.module.tsщоб надати XHRBackendяк ConnectionBackendпостачальника, так і RequestOptionsдля вашого власного класу Http:

import { HttpModule, RequestOptions, XHRBackend } from '@angular/http';
import { HttpService } from './services/http.service';
...
@NgModule({
  imports: [..],
  providers: [
    {
      provide: HttpService,
      useFactory: (backend: XHRBackend, options: RequestOptions) => {
        return new HttpService(backend, options);
      },
      deps: [XHRBackend, RequestOptions]
    }
  ],
  bootstrap: [ AppComponent ]
})

Після цього тепер ви можете використовувати свого користувальницького провайдера http у своїх послугах. Наприклад:

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

Ось вичерпний посібник - http://adonespitogo.com/articles/angular-2-extending-http-provider/


Цей підхід добре підходить для використання альтернативного постачальника класів. Замість того, щоб "забезпечити: HttpService", як у вашому модулі, ви можете замість цього використовувати "забезпечити: Http", що дозволяє вам працювати з Http, як зазвичай.
Кинджал Гілберта Аренаса

Як я можу додати додаткові властивості до цього розширеного класу http? Наприклад, маршрутизатор: Маршрутизатор або будь-які користувацькі сервіси для ін’єкцій.
shafeequemat

@shafeequemat Ви не можете зробити це, використовуючи це. Наприклад, ви можете визначити інший метод у своєму користувальницькому класі http setRouter(router). Або ви можете створити інший клас і ввести там свій спеціальний клас http замість протилежного.
Адонес Пітого

9

Для кута 5 і вище ми можемо використовувати HttpInterceptor для узагальнення операцій запиту та відповіді. Це допомагає нам уникнути дублювання:

1) Загальні заголовки

2) Вказання типу відповіді

3) Запит на запит

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

Ми можемо використовувати цей клас AuthHttpInterceptor в якості постачальника для 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

Краще пізно, ніж ніколи ... =)

Ви можете взяти концепцію розширеного BaseRequestOptions(звідси https://angular.io/docs/ts/latest/guide/server-communication.html#!#override-default-request-options ) та оновити заголовки "на льоту" "(не тільки в конструкторі). Ви можете використовувати властивість getter / setter "headers", що переосмислює так:

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

невелике оновлення: для кращої продуктивності ви можете розглянути можливість переміщення всіх статичних заголовків (наприклад, "Content-Type") до конструктора
Александр Ильинский

7

Так я зробив для встановлення токена з кожним запитом.

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

І зареєструватися в app.module.ts

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

6

Ось вдосконалена версія прийнятої відповіді, оновлена ​​для фіналу 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());
    }
}

Звичайно, його слід розширити для таких методів, як deleteі putякщо це потрібно (я ще не потребую їх на даний момент в моєму проекті).

Перевага полягає в тому, що у методах get// post/ є менший дублюваний код .

Зауважте, що в моєму випадку я використовую файли cookie для аутентифікації. Мені потрібен заголовок для i18n ( Accept-Languageзаголовок), оскільки багато значень, повернених нашим API, перекладені мовою користувача. У моєму додатку служба i18n вміщує мову, яку наразі обрав користувач.


як у вас з'явився tslint ігнорувати заголовки як дозволено?
Winnemucca

5

Як щодо утримання окремої служби, як описано нижче

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

і коли ви телефонуєте цьому з іншого місця, використовуйте this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

і ви побачите доданий заголовок, наприклад: - Авторизація наступним чином

введіть тут опис зображення


5

Після деякого розслідування я знайшов остаточний і найпростіший спосіб - розширити BaseRequestOptionsякий я віддаю перевагу.
Нижче описані способи, які я намагався і чомусь відмовився:
1. Розширити BaseRequestOptionsта додати динамічні заголовки в constructor(). Це не може працювати, якщо я ввійду. Він буде створений один раз. Тож це не динамічно.
2. розширити Http. З тієї ж причини, що і вище, я не можу додати динамічні заголовки в constructor(). І якщо я перепишу request(..)метод і встановлю заголовки, як це:

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

Вам просто потрібно перезаписати цей метод, але не кожен метод get / post / put.

3.І моє бажане рішення - це розширення BaseRequestOptionsта перезапис 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()функція буде викликана для кожного запиту.


Серед усіх наданих відповідей, це відповідь, яка привернула мою увагу, оскільки я вже пішов на рішення, яке ґрунтується на розширенні BaseRequestOptions. Однак, на жаль, це для мене не вийшло. можливі причини?
vigamage

спрацьовував. це рішення чудово, і у мене виникла проблема на моєму сервері. Мені довелося зробити декілька конфігурацій для запитів CORS перед польотом. см це посилання stackoverflow.com/a/43962690/3892439
vigamage

Як ви прив’язуєтесь AuthRequestOptionsдо решти програми? Я спробував поставити це в providersрозділ, але це нічого не зробило.
Травіс Паркс

Ви повинні перекрити постачальника RequestOptions, не BaseRequestOptions. angular.io/api/http/BaseRequestOptions
Travis Parks

У своєму додатку я просто розширюю BaseRequestOptions, і він вже розширює RequestOptions. Потім у app.module слід встановити провайдерів:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn

5

Хоча я відповідаю на це дуже пізно, але якщо хтось шукає більш легкого рішення.

Ми можемо використовувати angular2-jwt. angular2-jwt корисно автоматично приєднувати веб-маркер JSON (JWT) як заголовок авторизації під час подання HTTP-запитів із програми Angular 2.

Ми можемо встановити глобальні заголовки з розширеною опцією конфігурації

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

І надсилання за запитом як маркер, як

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

Було б корисно перейти на сторінку github.com/auth0/angular2-jwt#installation та адаптувати цю відповідь, використовуючи їх посібник із встановлення
Зуріель

4

Мені подобається ідея змінити параметри за замовчуванням, це здається хорошим рішенням.

Однак якщо ви до розширення Httpкласу. Не забудьте прочитати це!

Деякі відповіді тут насправді показують неправильне перевантаження request()методу, що може призвести до важких помилок та дивної поведінки. Я сам натрапив на це.

Це рішення засноване на request()реалізації методу в Angular 4.2.x, але має бути сумісним у майбутньому:

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;

  }

}

Зауважте, що я імпортую оригінальний клас таким чином import { Http as NgHttp } from '@angular/http';, щоб запобігти зіткненням імен.

Проблема, яка розглядається тут, полягає в тому, що request()метод має два різних підписи виклику. Коли Requestоб'єкт передається замість URL-адреси string, optionsаргумент ігнорується Angular. Тому обидва випадки повинні бути належним чином розроблені.

Ось приклад того, як зареєструвати цей переповнений клас у контейнері 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
  );
}

При такому підході ви можете вводити Httpклас нормально, але замінений клас буде магічно введений натомість. Це дозволяє легко інтегрувати своє рішення, не змінюючи інших частин програми (поліморфізм у дії).

Просто додайте httpProviderдо providersвластивості метаданих вашого модуля.


1

Найпростіший з усіх

Створіть config.tsфайл

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

Тоді на своєму service, просто імпортуйте config.tsфайл

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

Я думаю, що це було найпростіше і найбезпечніше.


0

Були деякі зміни для кутових 2.0.1 і вище:

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

Не виходить, спробував сам. Не дзвонить ні на що, окрім оновлення.
Філ

0

Я маю змогу вибрати більш спрощене рішення> Додати нові заголовки до параметрів за замовчуванням об'єднати або завантажити за допомогою функції api get (або іншої).

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

Звичайно, ви можете екстерналізувати ці заголовки за типовими параметрами або будь-яким у вашому класі. Це в API-експорті API класу експорту api.ts @Injectable () {}

Це дуже швидко, і це працює для мене. Я не хотів формату json / ld.


-4

Ви можете використовувати canActiveу своїх маршрутах так:

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

Взято з: https://auth0.com/blog/angular-2-authentication

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.