Angular - Đặt tiêu đề cho mọi yêu cầu


334

Tôi cần đặt một số tiêu đề Ủy quyền sau khi người dùng đã đăng nhập, cho mọi yêu cầu tiếp theo.


Để đặt tiêu đề cho một yêu cầu cụ thể,

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

Tài liệu tham khảo

Nhưng sẽ không khả thi khi đặt thủ công yêu cầu cho mọi yêu cầu theo cách này.

Làm cách nào để tôi đặt các tiêu đề được đặt khi người dùng đã đăng nhập và cũng xóa các tiêu đề đó khi đăng xuất?



Câu trả lời:


378

Để trả lời, bạn đặt câu hỏi rằng bạn có thể cung cấp dịch vụ bao bọc Httpđối tượng ban đầu từ Angular. Một cái gì đó giống như được mô tả dưới đây.

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

Và thay vì tiêm Httpđối tượng, bạn có thể tiêm cái này ( 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 );
    });
  }
}

Tôi cũng nghĩ rằng có thể thực hiện được điều gì đó bằng cách sử dụng nhiều nhà cung cấp cho Httplớp bằng cách cung cấp lớp của riêng bạn mở rộng Httpmột ... Xem liên kết này: http : // blog. -in-angular-2.html .


1
'this.http = http;' ở đâu đến từ đâu, tôi tin rằng chúng ta cần phải khai báo trước khi sử dụng?
co2f2e

1
Tiêu đề góc (đặt chức năng & nối thêm) là "bình thường hóa" khóa của tiêu đề và làm cho chữ thường. Từ Headers.d.ts: // "Bộ ký tự HTTP được xác định bởi các mã thông báo không phân biệt chữ hoa chữ thường" // Spec tại tools.ietf.org/html/rfc2616 Dành cho những người không có phần phụ trợ hoạt động bởi spec; đây là một bypass: let headersMap = .get (tùy chọn, 'headers._headersMap', new Map ()); headersMap.set ('Ủy quyền', [ .replace ( Bearer ${token}, / \ "/ g, '')]);
sangress

@DiegoUnanue Tôi đang sử dụng phiên bản cuối cùng của công việc triển khai Angular 2 và Thierry. Chỉ cần thay thế 'angular2' thành '@angular' trong các báo cáo nhập khẩu.
f123

Đánh dấu Pieszak- tôi có nên bao gồm các nhà cung cấp cho HttpClient không?
dùng3127109

bây giờ TS ném lỗi: `Đối số loại '{tiêu đề: Tiêu đề; } 'không thể gán cho tham số loại' RequestOptionsArss'`
elporfirio

142

HTTP chặn là bây giờ đã có thông qua mới HttpClienttừ @angular/common/http, như các phiên bản 4.3.x góc và xa hơn nữa .

Bây giờ khá đơn giản để thêm tiêu đề cho mọi yêu cầu:

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

Có một nguyên tắc bất biến , đó là lý do yêu cầu cần được nhân bản trước khi thiết lập một cái gì đó mới trên nó.

Vì các tiêu đề chỉnh sửa là một nhiệm vụ rất phổ biến, thực sự có một lối tắt cho nó (trong khi nhân bản yêu cầu):

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

Sau khi tạo thiết bị chặn, bạn nên đăng ký sử dụng HTTP_INTERCEPTORScung cấp.

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

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

Tôi đã triển khai điều này và khi thực hiện phục vụ, tôi có thể thấy các tiêu đề yêu cầu, tuy nhiên khi thực hiện và triển khai bên trong một con mèo, tôi không thấy các tiêu đề ... sử dụng spring-boot, các tiêu đề đã đi đâu?
naoru

1
Không biết có phải vì tôi đang làm việc với API nút Express, nhưng nó không hoạt động với tôi ngay cả với tài liệu Angular chính thức. : /
Maxime Lafarie

ERROR TypeError: CreatListFromArrayLike gọi trên phi đối tượng
DAG

1
Làm thế nào bạn sẽ tiêm bất cứ thứ gì vào httpInterceptor?
zaitsman

Tôi đã thực hiện những điều tương tự nhưng trong tiêu đề yêu cầu API PUT và DELETE không hoạt động đối với tôi.
Pooja

78

Mở rộng BaseRequestOptionscó thể giúp đỡ rất nhiều trong kịch bản này. Kiểm tra mã sau đây:

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

Điều này sẽ bao gồm 'My-Custom-Header' trong mỗi cuộc gọi.

Cập nhật:

Để có thể thay đổi tiêu đề bất cứ lúc nào bạn muốn thay vì mã ở trên, bạn cũng có thể sử dụng mã sau để thêm tiêu đề mới:

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

để xóa bạn có thể làm

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

Ngoài ra còn có một chức năng khác mà bạn có thể sử dụng để đặt giá trị:

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

Giải pháp trên vẫn không hoàn toàn hợp lệ trong bối cảnh bản thảo. _defaultHeaders được bảo vệ và không được sử dụng như thế này. Tôi muốn giới thiệu giải pháp trên để khắc phục nhanh nhưng về lâu dài tốt hơn nên viết trình bao bọc của riêng bạn xung quanh các cuộc gọi http cũng xử lý auth. Lấy ví dụ sau từ auth0 cái nào tốt hơn và sạch hơn.

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

Cập nhật - Tháng 6 năm 2018 Tôi thấy rất nhiều người sẽ tìm giải pháp này nhưng tôi sẽ khuyên khác. Áp dụng tiêu đề trên toàn cầu sẽ gửi mã thông báo xác thực cho mỗi cuộc gọi api đi ra từ ứng dụng của bạn. Vì vậy, các cuộc gọi api sẽ đến các plugin của bên thứ ba như intercom hoặc zendesk hoặc bất kỳ api nào khác cũng sẽ mang tiêu đề ủy quyền của bạn. Điều này có thể dẫn đến một lỗ hổng bảo mật lớn. Vì vậy, thay vào đó, hãy sử dụng thiết bị chặn trên toàn cầu nhưng kiểm tra thủ công xem cuộc gọi đi có hướng đến điểm cuối api của máy chủ của bạn hay không và sau đó đính kèm tiêu đề xác thực.


1
this .http._defaultOptions.headers.delete ('My-Custom-Header') ')
vào

2
@Dinistro vâng, bây giờ tôi sẽ không khuyên bạn làm điều này. Tôi đã phải đưa ra cách giải quyết này vì những hạn chế beta góc cạnh và thói quen kiểm soát luồng auth trên toàn cầu. Nhưng tôi tin bây giờ github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts có một giải pháp tốt hơn và sạch hơn.
vào

1
Vấn đề của việc sử dụng BaseRequestOptions là nó là hàm tạo của nó chỉ chạy một lần trong vòng đời ứng dụng trong trình duyệt. Vì vậy, nếu bạn muốn thay đổi giá trị tiêu đề trong thời gian (ví dụ: csrf_token), bạn không thể thực hiện theo cách này (thậm chí ghi đè phương thức hợp nhất trong lớp này không giúp ích gì :()
Kamil Kiełczewski 7/07/2016

1
Vấn đề là nếu bạn sử dụng thư viện của bên thứ 3 bao bọc truy cập HTTP trực tiếp cần phải được viết lại để sử dụng nó. Tôi vẫn không biết làm thế nào để vượt qua điều đó. Một máy bay đánh chặn là thực sự cần thiết. Không chắc chắn nếu có ai biết một cách tốt hơn.
Piotr Stulinski

6
Xin chào, trong angular4 _defaultOptionsđược bảo vệ nên không thể gọi từ dịch vụ
Andurit

24

Mặc dù tôi trả lời rất muộn nhưng nó có thể giúp người khác. Để tiêm tiêu đề cho tất cả các yêu cầu khi @NgModuleđược sử dụng, người ta có thể làm như sau:

(Tôi đã thử nghiệm điều này trong 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');
    }
}

Bây giờ hãy @NgModulelàm như sau:

@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
bạn cần @Injectable và xác định các tiêu đề trong lớp, tôi đã kiểm tra thành công bởi lớp xuất khẩu @Injectable () CustomRequestOptions mở rộng BaseRequestOptions {headers: Headers = new Headers ({'Authorization': 'xxx'}); }
EasonBlack

tốt, tôi đã làm điều này trong 2.0.0, không kiểm tra 2.0.1
EasonBlack

Lưu ý quan trọng ở đây tôi gặp phải một vấn đề trong đó không thể tiêm bất cứ thứ gì vào CustomRequestOptionsngay cả khi sử dụng @ Tiêm / @ Tiêm. Giải pháp tôi nhận ra là mở rộng RequestOptionschứ không phải BaseRequestOptions. Cung cấp BaseRequestOptionssẽ không hoạt động, nhưng RequestOptionsthay vào đó mở rộng làm cho DI hoạt động trở lại.
quốc hội

5
Giải pháp này rất đơn giản, nhưng nếu người dùng sẽ đăng xuất và đăng nhập lại và mã thông báo của anh ta thay đổi - nó sẽ không hoạt động nữa, vì Authorizationtiêu đề chỉ được đặt một lần trên ứng dụng init.
Alex Paramonov

Có, đúng @AlexeyVParamonov. Điều này chỉ hữu ích nếu mã thông báo đang được đặt một lần. Nếu không, chúng tôi sẽ viết các thiết bị chặn cho trường hợp như bạn đã nói.
Shashank Agrawal

15

Trong Angular 2.1.2tôi đã tiếp cận điều này bằng cách mở rộng các góc cạnh:

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

sau đó, trong Nhà cung cấp ứng dụng của mình, tôi đã có thể sử dụng Nhà máy tùy chỉnh để cung cấp '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(){

  }
}

bây giờ tôi không cần phải khai báo mọi phương thức http và có thể sử dụng httpnhư bình thường trong suốt ứng dụng của mình.


Câu trả lời này hoạt động tốt nhất đối với tôi vì tôi có thể lọc url đến máy chủ api của mình và chỉ thêm Mã xác thực vào các cuộc gọi được thực hiện. Tôi đã thay đổi yêu cầu thành: request (url: chuỗi | Yêu cầu, tùy chọn?: RequestOptionsArss): Có thể quan sát <Phản hồi> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {tùy chọn = this._setCustomHeaders (tùy chọn); } return super.request (url, tùy chọn)}
Chris Holcomb

Trong trường hợp của tôi, withCredentials và Headers được lấy từ tham số url trong phương thức yêu cầu. Tôi đã thay đổi mã như thế này: request (url: chuỗi | Yêu cầu, tùy chọn?: RequestOptionsArss): Có thể quan sát <Phản hồi> {tùy chọn = this._setCustomHeaders (tùy chọn); if (typeof (url) === "object") {(<Yêu cầu> url) .withCredentials = Options.withCredentials; (<Yêu cầu> url) .headers = Options.headers; } return super.request (url, tùy chọn)}
Argnist

Các request()phương pháp, mà bạn đang quá tải, có hai chữ ký cuộc gọi và các optionstài sản được sử dụng chỉ khi urlquy định như chuỗi. Trong trường hợp urllà một thể hiện của Request, optionstài sản chỉ bị bỏ qua. Điều này có thể dẫn đến một lỗi khó bắt. Xin vui lòng xem câu trả lời của tôi để biết thêm chi tiết.
Slava Fomin II


Điều này làm việc cho tôi cho đến khi góc 4.2. 4.3 Có thiết bị đánh chặn.
cabaji99

12

Tạo một lớp http tùy chỉnh bằng cách mở rộng HttpNhà cung cấp Angular 2 và chỉ cần ghi đè constructorrequestphương thức trong lớp http tùy chỉnh của bạn. Ví dụ dưới đây thêm Authorizationtiêu đề trong mỗi yêu cầu 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);
    };
  }
}

Sau đó định cấu hình chính của bạn app.module.tsđể cung cấp với XHRBackendtư cách là ConnectionBackendnhà cung cấp và RequestOptionslớp http tùy chỉnh của bạn:

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

Sau đó, bây giờ bạn có thể sử dụng nhà cung cấp http tùy chỉnh trong các dịch vụ của mình. Ví dụ:

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

Dưới đây là hướng dẫn toàn diện - http://adonespitogo.com/articles/angular-2-extending-http-provider/


Cách tiếp cận này rất phù hợp để sử dụng một nhà cung cấp lớp thay thế. Thay vì "cung cấp: httpService" như bạn có trong mô-đun của mình, thay vào đó, bạn có thể sử dụng "cung cấp: http" cho phép bạn làm việc với http như bạn thường làm.
Dao găm Gilbert Arenas

Làm cách nào tôi có thể thêm các thuộc tính bổ sung cho lớp http mở rộng này? Ví dụ: bộ định tuyến: Bộ định tuyến hoặc bất kỳ dịch vụ tiêm tùy chỉnh nào.
shafeequemat

@shafeequemat Bạn không thể làm điều đó bằng cách sử dụng này. Bạn có thể định nghĩa một phương thức khác trong lớp http tùy chỉnh của bạn, ví dụ setRouter(router). Hoặc bạn có thể tạo một lớp khác và đưa lớp http tùy chỉnh của mình vào đó thay vì ngược lại.
Adones Pitogo

9

Đối với Angular 5 trở lên, chúng ta có thể sử dụng HttpInterceptor để khái quát các hoạt động yêu cầu và phản hồi. Điều này giúp chúng tôi tránh trùng lặp:

1) Tiêu đề chung

2) Chỉ định loại phản hồi

3) Yêu cầu truy vấn

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

Chúng tôi có thể sử dụng lớp AuthHttpInterceptor này làm nhà cung cấp cho các 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

Muộn còn hơn không bao giờ ... =)

Bạn có thể lấy khái niệm mở rộng BaseRequestOptions(từ đây https://angular.io/docs/ts/latest/guide/server-cransication.html#!#override-default-request-options ) và làm mới các tiêu đề "một cách nhanh chóng "(Không chỉ trong constructor). Bạn có thể sử dụng ghi đè thuộc tính "tiêu đề" getter / setter như thế này:

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

ít update: cho hiệu suất tốt hơn bạn có thể xem xét di chuyển tất cả các tiêu đề tĩnh (như 'Content-Type') để các nhà xây dựng
Александр Ильинский

7

Đây là cách tôi đã làm để thiết lập mã thông báo với mọi yêu cầu.

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

Và đăng ký trong app.module.ts

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

6

Đây là phiên bản cải tiến của câu trả lời được chấp nhận, được cập nhật cho trận chung kết 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());
    }
}

Tất nhiên nó nên được mở rộng cho các phương thức như deleteputnếu cần (tôi chưa cần đến chúng tại thời điểm này trong dự án của tôi).

Ưu điểm là có ít mã trùng lặp trong các phương thức get/ post/ ....

Lưu ý rằng trong trường hợp của tôi, tôi sử dụng cookie để xác thực. Tôi cần tiêu đề cho i18n ( Accept-Languagetiêu đề) vì nhiều giá trị được API của chúng tôi trả về được dịch theo ngôn ngữ của người dùng. Trong ứng dụng của tôi, dịch vụ i18n giữ ngôn ngữ mà người dùng hiện đang chọn.


Làm thế nào bạn có được tslint để bỏ qua các tiêu đề như cho phép?
Winnemucca

5

Cách giữ một dịch vụ riêng biệt như sau

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

và khi bạn gọi nó từ nơi khác sử dụng this.httpClientService.addHeader("Authorization", "Bearer " + this.tok);

và bạn sẽ thấy tiêu đề được thêm vào, ví dụ: - Ủy quyền như sau

nhập mô tả hình ảnh ở đây


5

Sau một số điều tra, tôi tìm thấy cách cuối cùng và dễ dàng nhất là mở rộng BaseRequestOptionscái mà tôi thích.
Sau đây là những cách tôi đã thử và từ bỏ vì một số lý do:
1. mở rộng BaseRequestOptionsvà thêm các tiêu đề động vào constructor(). Nó không thể hoạt động nếu tôi đăng nhập. Nó sẽ được tạo ra một lần. Vì vậy, nó không năng động.
2. mở rộng Http. Cùng một lý do như trên, tôi không thể thêm các tiêu đề động constructor(). Và nếu tôi viết lại request(..)phương thức và đặt các tiêu đề, như thế này:

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

Bạn chỉ cần ghi đè lên phương thức này, nhưng không phải mọi phương thức get / post / put.

3.Và giải pháp ưa thích của tôi là mở rộng BaseRequestOptionsvà ghi đè 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;
 }
}

này merge()chức năng sẽ được gọi cho mọi yêu cầu.


Trong số tất cả các câu trả lời được đưa ra, đây là câu trả lời khiến tôi chú ý vì tôi đã đi đến một giải pháp dựa trên việc mở rộng BaseRequestOptions. Tuy nhiên, thật đáng buồn, điều này đã không làm việc cho tôi. bất kỳ lý do có thể?
cảnh giác

làm cho nó hoạt động giải pháp này là tốt và tôi đã có một vấn đề trong máy chủ của tôi. Tôi đã phải thực hiện một số cấu hình cho các yêu cầu trước chuyến bay của CORS. tham khảo liên kết này stackoverflow.com/a/43942690/3892439
vigamage

Làm thế nào để bạn buộc AuthRequestOptions vào phần còn lại của ứng dụng? Tôi đã cố gắng đưa phần này vào providersphần nhưng nó không làm gì cả.
Công viên Travis

Bạn phải ghi đè nhà cung cấp cho RequestOptions, khôngBaseRequestOptions . angular.io/api/http/BaseRequestOptions
Công viên Travis

Trong ứng dụng của mình, tôi chỉ mở rộng BaseRequestOptions và nó đã mở rộng RequestOptions. Sau đó, trong app.module, bạn nên đặt các nhà cung cấp:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn

5

Mặc dù tôi đang trả lời điều này rất muộn nhưng nếu có ai đang tìm kiếm một giải pháp dễ dàng hơn.

Chúng ta có thể sử dụng angular2-jwt. angular2-jwt rất hữu ích khi tự động đính kèm Mã thông báo Web JSON (JWT) làm tiêu đề Ủy quyền khi thực hiện các yêu cầu HTTP từ ứng dụng Angular 2.

Chúng tôi có thể đặt tiêu đề toàn cầu với tùy chọn cấu hình nâng cao

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

Và gửi mỗi mã thông báo yêu cầu như

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

sẽ hữu ích cho goto github.com/auth0/angular2-jwt#installation và điều chỉnh câu trả lời này bằng hướng dẫn cài đặt của họ
Zuriel

4

Tôi thích ý tưởng ghi đè các tùy chọn mặc định, đây có vẻ là một giải pháp tốt.

Tuy nhiên, nếu bạn đang mở rộng Httplớp học. Hãy chắc chắn để đọc điều này thông qua!

Một số câu trả lời ở đây thực sự cho thấy quá tải request()phương pháp không chính xác , điều này có thể dẫn đến một lỗi khó bắt và hành vi kỳ lạ. Tôi đã vấp phải điều này bản thân mình.

Giải pháp này dựa trên request()việc triển khai phương pháp trong Angular 4.2.x, nhưng phải tương thích với tương lai:

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;

  }

}

Lưu ý rằng tôi đang nhập lớp gốc theo cách này import { Http as NgHttp } from '@angular/http'; để ngăn chặn xung đột tên.

Vấn đề được giải quyết ở đây là request()phương pháp này có hai chữ ký cuộc gọi khác nhau. Khi Requestđối tượng được thông qua thay vì URL string, optionsđối số bị bỏ qua bởi Angular. Vì vậy, cả hai trường hợp phải được xử lý đúng.

Và đây là ví dụ về cách đăng ký lớp bị ghi đè này với DI container:

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

Với cách tiếp cận như vậy bạn có thể tiêm Http lớp bình thường, nhưng lớp bị ghi đè của bạn sẽ được tiêm một cách kỳ diệu thay vào đó. Điều này cho phép bạn tích hợp giải pháp của mình một cách dễ dàng mà không thay đổi các phần khác của ứng dụng (tính đa hình trong hành động).

Chỉ cần thêm httpProvidervào thuộc providerstính của siêu dữ liệu mô-đun của bạn.


1

Đơn giản nhất trong tất cả

Tạo một config.tstập tin

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

Sau đó, bạn servicechỉ cần nhập config.tstệp

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

Tôi nghĩ đó là cách đơn giản và an toàn nhất.


0

Có một số thay đổi cho góc 2.0.1 trở lên:

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

Không hoạt động, đã thử bản thân mình. Không được gọi bất cứ điều gì ngoài làm mới.
Phil

0

Tôi có thể chọn một giải pháp đơn giản hơn> Thêm một Tiêu đề mới vào các tùy chọn mặc định hợp nhất hoặc tải bằng chức năng api get (hoặc khác) của bạn.

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

Tất nhiên, bạn có thể bên ngoài Tiêu đề này trong các tùy chọn mặc định hoặc bất cứ điều gì trong lớp của bạn. Đây là trong API lớp xuất khẩu api.ts @Injectable () được tạo ra {} {}

Nó rất nhanh và nó làm việc cho tôi. Tôi không muốn định dạng json / ld.


-4

Bạn có thể sử dụng canActivetrong các tuyến đường của mình, như vậy:

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

Lấy từ: https://auth0.com/blog/angular-2-authentication

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.