Angular - ตั้งส่วนหัวสำหรับทุกคำขอ


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-เชิงมุม-2.html


1
อยู่ที่ไหน 'this.http = http;' มาจากฉันเชื่อว่าเราต้องประกาศก่อนใช้?
co2f2e

1
ส่วนหัวมุม (ฟังก์ชั่นการตั้งค่าและผนวก) คือ "normalizing" ปุ่มส่วนหัวและทำให้มันเป็นกรณีที่ต่ำกว่า จาก Headers.d.ts: // "ชุดอักขระ HTTP ถูกระบุโดยโทเค็นแบบ case-insensitive" // Spec ที่tools.ietf.org/html/rfc2616 สำหรับผู้ที่ไม่มีแบ็กเอนด์ที่ใช้งานได้กับสเป็ค; นี่คือบายพาส: let headersMap = .get (ตัวเลือก, 'headers._headersMap', แผนที่ใหม่ () headersMap.set ('การอนุญาต', [ .replace ( Bearer ${token}, / \ "/ g, '')])
;;

@DiegoUnanue ฉันกำลังใช้งานเวอร์ชันสุดท้ายของการใช้งาน Angular 2 และ Thierry เพียงแทนที่ 'angular2' เป็น '@angular' ในคำสั่งการนำเข้า
f123

Mark Pieszak - ฉันควรรวมผู้ให้บริการสำหรับ HttpClient ด้วยหรือไม่
user3127109

ตอนนี้ TS พ่นข้อผิดพลาด: `อาร์กิวเมนต์ประเภท '{ส่วนหัว: ส่วนหัว; } 'ไม่สามารถกำหนดให้กับพารามิเตอร์ประเภท' 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 และปรับใช้ภายในแมวตัวผู้ฉันไม่เห็นส่วนหัว ... โดยใช้ spring-boot ส่วนหัวหายไปไหน
naoru

1
ไม่ทราบว่าเป็นเพราะฉันทำงานกับ Express node API หรือไม่ แต่มันไม่ได้ผลแม้แต่กับ Angular doc : /
Maxime Lafarie

ข้อผิดพลาด 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 })
]);

ซึ่งควรรวมถึง 'My-Custom-Header' ในทุกการโทร

ปรับปรุง:

เพื่อให้สามารถเปลี่ยนส่วนหัวได้ทุกเวลาที่คุณต้องการแทนที่จะเป็นรหัสด้านบนคุณสามารถใช้รหัสต่อไปนี้เพื่อเพิ่มส่วนหัวใหม่:

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

เพื่อลบคุณสามารถทำได้

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

นอกจากนี้ยังมีฟังก์ชั่นอื่นที่คุณสามารถใช้เพื่อตั้งค่า:

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

การแก้ปัญหาข้างต้นยังคงไม่ถูกต้องในบริบทของ typescript _defaultHeaders ได้รับการคุ้มครองและไม่ควรใช้เช่นนี้ ฉันอยากจะแนะนำวิธีแก้ไขปัญหาข้างต้นสำหรับการแก้ไขอย่างรวดเร็ว แต่ในระยะยาวมันจะดีกว่าถ้าคุณเขียน wrapper ของคุณเองผ่านการโทร http ซึ่งจัดการกับ auth ใช้ตัวอย่างต่อไปนี้จาก auth0 ซึ่งดีกว่าและสะอาด

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

อัปเดต - มิถุนายน 2561 ฉันเห็นผู้คนจำนวนมากกำลังใช้วิธีนี้ แต่ฉันจะให้คำแนะนำเป็นอย่างอื่น ส่วนหัวที่ต่อท้ายทั่วโลกจะส่งโทเค็นการตรวจสอบสิทธิ์ไปยังการเรียก API ทุกครั้งที่ออกจากแอปของคุณ ดังนั้น api จึงโทรไปยังปลั๊กอินของบุคคลที่สามเช่นอินเตอร์คอมหรือ zendesk หรือ api อื่น ๆ จะดำเนินการส่วนหัวการอนุญาตของคุณ ซึ่งอาจส่งผลให้เกิดข้อบกพร่องด้านความปลอดภัยขนาดใหญ่ ดังนั้นให้ใช้ interceptor ทั่วโลก แต่ตรวจสอบด้วยตนเองว่าการโทรออกไปยังจุดปลาย api ของเซิร์ฟเวอร์หรือไม่จากนั้นแนบส่วนหัวรับรองความถูกต้อง


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) คุณไม่สามารถทำได้ด้วยวิธีนี้ (แม้จะแทนที่วิธีการรวมในชั้นนี้ไม่ช่วย :()
Kamil Kiełczewski

1
ปัญหาคือว่าถ้าคุณใช้ไลบรารีของบุคคลที่สาม wrapper ที่เข้าถึง HTTP โดยตรงจะต้องมีการเขียนใหม่เพื่อใช้งาน ฉันยังไม่รู้วิธีหลีกเลี่ยงสิ่งนั้น ตัวดักจำเป็นต้องมีจริงๆ ไม่แน่ใจว่าใครรู้วิธีที่ดีกว่า
Piotr Stulinski

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 ขยาย BaseRequestOptions {ส่วนหัว: ส่วนหัว = ส่วนหัวใหม่ ({'การอนุญาต': 'xxx'}); }
EasonBlack

ดีฉันทำสิ่งนี้ใน 2.0.0 ไม่ได้ตรวจสอบ 2.0.1
EasonBlack

หมายเหตุสำคัญที่นี่ฉันพบปัญหาที่เป็นไปไม่ได้ที่จะแทรกสิ่งใด ๆ เข้าไปCustomRequestOptionsแม้ในขณะที่ใช้ @ Inject / @ Injectable วิธีการแก้ปัญหาที่ผมรู้มาจะขยายไม่RequestOptions BaseRequestOptionsการให้บริการBaseRequestOptionsจะไม่ทำงาน แต่การขยายRequestOptionsแทนทำให้ DI ทำงานได้อีกครั้ง
รัฐสภา

5
วิธีแก้ปัญหานี้ง่าย แต่ถ้าผู้ใช้จะออกจากระบบและกลับเข้าสู่ระบบและโทเค็นการเปลี่ยนแปลง - มันจะไม่ทำงานอีกต่อไปเพราะAuthorizationส่วนหัวตั้งเพียงครั้งเดียวในแอปพลิเคชัน init
Alex Paramonov

ใช่แก้ไข @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 | Request, options ?: RequestOptionsArgs): สังเกตได้ <Response> {var _url: string = url.toString (); if (_url.indexOf ('api.myserver.com')> -1) {options = this._setCustomHeaders (ตัวเลือก); } return super.request (url, options)}
Chris Holcomb

ในกรณีของฉัน withCredentials และส่วนหัวถูกพรากไปจากพารามิเตอร์ url ในวิธีการร้องขอ ฉันเปลี่ยนรหัสเช่นนี้: request (url: string | Request, options ?: RequestOptionsArgs): สังเกตได้ <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คุณสมบัติถูกละเว้นเพียง สิ่งนี้อาจนำไปสู่ข้อผิดพลาดที่ยากต่อการตรวจจับ โปรดดูคำตอบของฉันสำหรับรายละเอียดเพิ่มเติม
Slava Fomin II

ทราบว่าการแก้ปัญหานี้มีบางประเด็นที่มีแพลตฟอร์มเซิร์ฟเวอร์ มีวิธีแก้ไขเฉพาะหน้าเพื่อหลีกเลี่ยงอย่างไรก็ตาม
Alireza Mirian

สิ่งนี้ใช้ได้กับฉันจนกระทั่ง 4.2 เชิงมุม 4.3 มีตัวดัก
cabaji99

12

สร้างคลาส Http ที่กำหนดเองโดยขยายHttpผู้ให้บริการเชิงมุม 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 ได้ตามปกติ
Gilbert Arenas Dagger

ฉันจะเพิ่มคุณสมบัติเพิ่มเติมให้กับคลาส http ที่ขยายเพิ่มนี้ได้อย่างไร ตัวอย่างเช่นเราเตอร์: เราเตอร์หรือบริการฉีดที่กำหนดเองใด ๆ
shafeequemat

@shafeequemat คุณไม่สามารถทำได้โดยใช้สิ่งนี้ คุณสามารถกำหนดวิธีอื่นในชั้นเรียน http setRouter(router)กำหนดเองของคุณตัวอย่างเช่น หรือคุณสามารถสร้างคลาสอื่นและฉีดคลาส http ที่กำหนดเองของคุณในนั้นแทนที่จะเป็นตรงกันข้าม
Adones Pitogo

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 นี้เป็นผู้ให้บริการสำหรับ 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 };

อัปเดตเล็กน้อย: เพื่อประสิทธิภาพที่ดีขึ้นคุณอาจพิจารณาย้ายส่วนหัวแบบคงที่ทั้งหมด (เช่น 'เนื้อหาชนิด') ไปยังตัวสร้าง
АлександрИльинский

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 วิธีที่จะละเว้นส่วนหัวตามที่ได้รับอนุญาต?
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ส่วนนี้ แต่ก็ไม่ได้ทำอะไรเลย
Travis Parks

คุณต้องแทนที่ผู้ให้บริการสำหรับไม่RequestOptions angular.io/api/http/BaseRequestOptionsBaseRequestOptions
เทรวิสพาร์ค

ในแอพของฉันฉันเพิ่งขยาย BaseRequestOptions และขยาย RequestOptions ไปแล้ว จากนั้นใน app.module คุณควรตั้งผู้ให้บริการ:{ provide: RequestOptions, useClass: AuthRequestOptions }
Mavlarn

5

แม้ว่าฉันจะตอบคำถามนี้ช้ามาก แต่ถ้าใครกำลังมองหาทางออกที่ง่ายกว่า

เราสามารถใช้ angular2-jwt angular2-jwt มีประโยชน์โดยอัตโนมัติการแนบ JSON Web Token (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')
    );
}

จะเป็นประโยชน์กับ goto github.com/auth0/angular2-jwt#installationและปรับคำตอบนี้โดยใช้คำแนะนำการติดตั้ง
Zuriel

4

ฉันชอบความคิดที่จะแทนที่ตัวเลือกเริ่มต้นดูเหมือนว่าจะเป็นทางออกที่ดี

อย่างไรก็ตามหากคุณกำลังขยายการ Httpคลาสขึ้นไป อย่าลืมอ่านข้อความนี้ด้วย!

บางคำตอบที่นี่แสดงว่ามีการโหลดมากเกินไปที่ไม่ถูกต้อง request()วิธีการซึ่งอาจนำไปสู่ข้อผิดพลาดที่จับยากและพฤติกรรมแปลก ๆ ฉันพบสิ่งนี้ด้วยตัวเอง

โซลูชันนี้ใช้request()วิธีการนำไปใช้ในเชิงมุม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อาร์กิวเมนต์จะถูกละเว้นโดยเชิงมุม ดังนั้นทั้งสองกรณีจะต้องได้รับการจัดการอย่างเหมาะสม

และนี่คือตัวอย่างของการลงทะเบียนคลาสที่ถูกแทนที่ด้วยคอนเทนเนอร์ 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คลาสปกติ แต่คลาสที่แทนที่ของคุณจะถูกฉีดอย่างน่าอัศจรรย์แทน สิ่งนี้ช่วยให้คุณสามารถรวมโซลูชันของคุณได้อย่างง่ายดายโดยไม่ต้องเปลี่ยนส่วนอื่น ๆ ของแอปพลิเคชัน (polymorphism in action)

เพียงแค่เพิ่ม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 { }

ไม่ทำงานลองด้วยตัวเอง ไม่ได้รับการเรียกร้องอะไรเลยนอกจากการรีเฟรช
Phil

0

ฉันสามารถเลือกวิธีที่ง่ายกว่า> เพิ่มส่วนหัวใหม่ให้กับตัวเลือกค่าเริ่มต้นผสานหรือโหลดโดยฟังก์ชั่น api get (หรืออื่น ๆ ) ของคุณ

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

แน่นอนว่าคุณสามารถทำให้ส่วนหัวนี้เป็นตัวเลือกเริ่มต้นหรืออะไรก็ได้ในชั้นเรียนของคุณ นี่คือ Ionic ที่สร้างขึ้น api.ts @Injectable () API การส่งออกคลาส {}

มันเร็วมากและใช้งานได้สำหรับฉัน ฉันไม่ต้องการรูปแบบ 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.