我需要在用户登录后为每个后续请求设置一些授权标头.
要为特定请求设置标头,
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
参考
但是以这种方式为每个请求手动设置请求标头是不可行的.
如何在用户登录后设置标头集,并在注销时删除这些标头?
要回答,您质疑您可以提供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.
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, next: HttpHandler): Observable > { // 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 {}
BaseRequestOptions
在这种情况下,扩展可能会有很大帮助.查看以下代码:
import {provide} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {HTTP_PROVIDERS, Headers, Http, BaseRequestOptions} from 'angular2/http'; import {AppCmp} from './components/app/app'; class MyRequestOptions extends BaseRequestOptions { constructor () { super(); this.headers.append('My-Custom-Header','MyCustomHeaderValue'); } } bootstrap(AppCmp, [ ROUTER_PROVIDERS, HTTP_PROVIDERS, provide(RequestOptions, { useClass: MyRequestOptions }) ]);
这应包括每次通话中的"我的自定义标题".
更新:
为了能够随时更改标题而不是上面的代码,您还可以使用以下代码添加新标题:
this.http._defaultOptions.headers.append('Authorization', 'token');
删除你可以做
this.http._defaultOptions.headers.delete('Authorization');
还有另一个函数可用于设置值:
this.http._defaultOptions.headers.set('Authorization', 'token');
上述解决方案在打字稿上下文中仍然不完全有效._defaultHeaders受到保护,不应该像这样使用.我建议使用上面的解决方案进行快速修复,但从长远来看,最好在http调用周围编写自己的包装器,它也会处理auth.从auth0获取以下示例,该示例更好,更干净.
https://github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts
更新 - 2018年6月 我看到很多人都在寻求这个解决方案,但我会建议不这样做.全局追加标头会向您的应用发出的每个 api呼叫发送身份验证令牌.因此api调用第三方插件如内部通信或zendesk或任何其他api也将带有您的授权标头.这可能会导致严重的安全漏洞.所以相反,全局使用拦截器,但是如果传出呼叫是否朝向服务器的api端点,则手动检查,然后附加auth头.
虽然我很晚才回答它,但它可能对其他人有所帮助.要在@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] })
在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{ 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
在我的应用程序中正常使用.
通过扩展Angular 2 Http
Provider 创建一个自定义Http类,并简单地覆盖自定义Http类中的constructor
and request
方法.以下示例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{ 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/
迟到总比没好...... =)
您可以采用扩展的概念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 };
对于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, next: HttpHandler): Observable > { 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 ) => { 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 { }
以下是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{ 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 { let headers = new Headers(); this.createAcceptLanguageHeader(headers); let options = new BaseRequestOptions(); options.headers = headers; options.url = url; options.method = method; options.body = body; options.withCredentials = true; let request = new Request(options); return this.http.request(request); } // set the accept-language header using the value from i18n service that holds the language currently selected by the user private createAcceptLanguageHeader(headers:Headers) { headers.append('Accept-Language', this.i18n.getCurrentLang()); } }
当然,它应该扩展到类似的方法delete
,put
如果需要的话(我现在还不需要它们在我的项目中).
优点是get
/ post
/ ...方法中的重复代码较少.
请注意,在我的情况下,我使用cookie进行身份验证.我需要i18n(Accept-Language
标题)的标题,因为我们的API返回的许多值都是用用户的语言翻译的.在我的应用程序中,i18n服务保存用户当前选择的语言.
这就是我为每个请求设置令牌的方法.
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 { }
如何保持单独的服务如下
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);
并且您将看到添加的标题,例如: - 授权如下
经过一番调查,我发现最后也是最简单的方法就是延伸BaseRequestOptions
我喜欢的.
以下是我尝试和放弃的方式:
1.扩展BaseRequestOptions
,并添加动态标头constructor()
.如果我登录,它无法工作.它将被创建一次.所以它不是动态的.
2.延伸Http
.与上面相同的原因,我无法添加动态标头constructor()
.如果我重写request(..)
方法,并设置标题,像这样:
request(url: string|Request, options?: RequestOptionsArgs): Observable{ 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()
每个请求都会调用此函数.
虽然我很晚才回答这个问题,但是如果有人在寻求更简单的解决方案.
我们可以使用angular2-jwt.在从Angular 2应用程序发出HTTP请求时,angular2-jwt可自动将JSON Web令牌(JWT)作为授权标头附加.
我们可以使用高级配置选项设置全局标头
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') ); }