Угловой - Установить заголовки для каждого запроса

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 -in-angular-2.html .

Тьерри Темплиер
источник
1
где это 'this.http = http;' приходит, я считаю, что мы должны объявить это перед использованием?
co2f2e
1
Угловые заголовки (функции set и append) «нормализуют» ключ заголовка и делают его строчными. From Headers.d.ts: // «Наборы символов HTTP идентифицируются с помощью нечувствительных к регистру токенов» // Spec на tools.ietf.org/html/rfc2616 Для тех, у кого нет серверной части, работающей по спецификации; вот обходной путь: let headersMap = .get (options, 'headers._headersMap', new Map ()); headersMap.set ('Авторизация', [ .replace ( Bearer ${token}, / \ "/ g, '')]);
sangress
@DiegoUnanue Я использую финальную версию Angular 2 и работ по реализации Thierry. Просто замените «angular2» на «@angular» в операторах импорта.
f123
Марк Pieszak - я должен включить поставщиков для 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 serve я могу видеть заголовки запроса, однако при выполнении ng b prod и развертывании внутри tomcat я не вижу заголовков ... используя spring-boot, куда делись заголовки?
Наору
1
Не знаю, так ли это, потому что я работаю с API-интерфейсом Express-узла, но он не работает для меня даже с официальным Angular doc. : /
Максим Лафари
ОШИБКА TypeError: CreateListFromArrayLike вызван для необъекта
DAG
1
Как бы вы внедрить что-нибудь в HttpInterceptor?
Зайцман
Я реализовал то же самое, но в PUT и DELETE API заголовок запроса не работает для меня.
Пуджа
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 })
]);

Это должно включать «My-Custom-Header» в каждом вызове.

Обновить:

Чтобы иметь возможность изменить заголовок в любое время, вместо кода выше, вы также можете использовать следующий код для добавления нового заголовка:

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

удалить вы можете сделать

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

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

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

Вышеупомянутое решение все еще не полностью верно в контексте машинописи. _defaultHeaders защищен и не должен использоваться таким образом. Я бы порекомендовал вышеупомянутое решение для быстрого исправления, но в долгосрочной перспективе лучше написать свою собственную оболочку для вызовов http, которая также обрабатывает аутентификацию. Возьмите следующий пример из auth0, который лучше и чище.

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

Обновление - июнь 2018 г. Я вижу, что многие люди выбирают это решение, но я бы посоветовал иначе. Добавление заголовка глобально будет отправлять токен авторизации на каждый вызов API, исходящий из вашего приложения. Поэтому вызовы API, передаваемые сторонним плагинам, таким как внутренняя связь, Zendesk или любой другой API, также будут содержать ваш заголовок авторизации. Это может привести к серьезному недостатку безопасности. Поэтому вместо этого используйте перехватчик глобально, но проверьте вручную, направлен ли исходящий вызов к конечной точке API вашего сервера, и затем присоедините заголовок аутентификации.

Anit
источник
1
this.http._defaultOptions.headers.delete ('My-Custom-Header') Таким образом, описанный выше процесс можно сократить с помощью следующего кода this.http._defaultOptions.headers.append ('My-New-Custom-Header', 'newvalue «)
Anit
2
@Dinistro да, теперь я не рекомендую себе делать это. Мне пришлось придумать этот обходной путь из-за угловых ограничений бета-версии и моей привычки контролировать поток аутентификации во всем мире. Но я верю, что сейчас у github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts есть лучшее и чистое решение.
Anit
1
Проблема использования BaseRequestOptions заключается в том, что его конструктор запускается только один раз за время существования приложения в браузере. Поэтому, если вы хотите изменить значение заголовка во время (например, csrf_token), вы не можете сделать это таким образом (даже переопределение метода слияния в этом классе не помогает :()
Камиль Килчевски
1
Проблема заключается в том, что если вы используете обертку, сторонние библиотеки, которые обращаются к HTTP напрямую, необходимо переписать, чтобы использовать его. Я до сих пор не знаю, как обойти это. Перехватчик действительно нужен. Не уверен, если кто-нибудь знает лучший способ.
Петр Стулинский
6
Привет, в angular4 _defaultOptionsзащищен, поэтому не может быть вызван из службы
Andurit
24

Хотя я отвечаю на это очень поздно, но это может помочь кому-то еще. Чтобы внедрить заголовки во все запросы, когда они @NgModuleиспользуются, можно сделать следующее:

(Я проверял это в 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');
    }
}

Теперь @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 extends BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack
ну, я сделал это в 2.0.0, не проверял 2.0.1
EasonBlack
Важное замечание: здесь я столкнулся с проблемой, из-за которой было невозможно внедрить что-либо CustomRequestOptionsдаже при использовании @ Inject / @ Injectable. Решение, которое я понял, было расширить RequestOptions, а не BaseRequestOptions. Предоставление BaseRequestOptionsне будет работать, но расширение RequestOptionsвместо этого заставляет DI работать снова.
парламент
5
Это простое решение, но если пользователь выйдет из системы и снова войдет в систему, а его токен изменится - он больше не будет работать, поскольку Authorizationзаголовок устанавливается только один раз в приложении init.
Алексей Парамонов
Да, правильно @AlexeyVParamonov. Это полезно, только если токен устанавливается один раз. В противном случае мы напишем перехватчики для дела, как вы сказали.
Шашанк Агравал
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как обычно для всего приложения.

Jonnie
источник
Этот ответ работал лучше всего для меня, так как я смог отфильтровать URL-адрес моего сервера API и добавить только токен аутентификации к сделанным вызовам. Я изменил запрос на: request (url: string | Request, options ?: RequestOptionsArgs): Observable <Response> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {options = this._setCustomHeaders (options); } return super.request (url, options)}
Крис Холкомб
В моем случае withCredentials и Headers были взяты из параметра url в методе запроса. Я изменил код следующим образом: 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
request()Метод, который вы перегрузка, имеет две подписи вызова и optionsсвойство используется только тогда , когда urlуказано в виде строки. В случае, когда urlэто экземпляр Request, optionsсвойство просто игнорируется. Это может привести к сложным ошибкам. Пожалуйста, смотрите мой ответ для более подробной информации.
Слава Фомин II
Обратите внимание, что в этом решении есть некоторые проблемы с серверной платформой . Однако есть обходные пути, чтобы этого избежать .
Алиреза Мириан
Это работало для меня до угловых 4.2. 4.3 Имеет перехватчики.
cabaji99
12

Создайте пользовательский класс Http, расширив Angular 2 HttpProvider, и просто переопределите метод constructorand 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);
    };
  }
}

Затем настройте ваш main 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

Для Angular 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 в качестве поставщика для HttpInterceptor:

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
источник
8

Лучше поздно, чем никогда ... =)

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

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/ ... меньше дублирующегося кода .

Обратите внимание, что в моем случае я использую куки для аутентификации. Мне нужен заголовок для i18n ( Accept-Languageзаголовок), потому что многие значения, возвращаемые нашим API, переводятся на язык пользователя. В моем приложении сервис i18n содержит язык, выбранный пользователем.

Пьер Генри
источник
как ты заставил tslint игнорировать заголовки как let?
Виннемукка
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);

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

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

co2f2e
источник
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()функция будет вызываться для каждого запроса.

Mavlarn
источник
Среди всех ответов, это ответ, который привлек мое внимание, так как я уже искал решение, основанное на расширении BaseRequestOptions. Однако, к сожалению, это не сработало для меня. какие-либо возможные причины?
vigamage
получил это работает. это решение хорошо, и у меня была проблема на моем сервере. Я должен был сделать некоторые конфигурации для запросов перед полетом CORS. обратитесь по этой ссылке stackoverflow.com/a/43962690/3892439
vigamage
Как вы привязываетесь AuthRequestOptionsк остальной части приложения? Я попытался поместить это в providersраздел, но он ничего не сделал.
Трэвис Паркс
Вы должны переопределить поставщика RequestOptions, а не BaseRequestOptions. angular.io/api/http/BaseRequestOptions
Трэвис Паркс,
В моем приложении я просто расширяю 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')
    );
}
KNimhan
источник
было бы полезно
Zuriel
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свойство метаданных вашего модуля.

Слава Фомин II
источник
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(endpoint: string, params?: any, options?: RequestOptions) {
  if (!options) {
    options = new RequestOptions();
    options.headers = new Headers( { "Accept": "application/json" } ); <<<<
  }
  // [...] 
}

Конечно, вы можете использовать этот заголовок в параметрах по умолчанию или в вашем классе. Это в сгенерированном Ionic 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

В
источник