用Angular全球处理401s

在我的Angular 2项目中,我从服务返回一个Observable的API调用。 调用代码然后订阅这个可观察的。 例如:

getCampaigns(): Observable<Campaign[]> { return this.http.get('/campaigns').map(res => res.json()); } 

假设服务器返回一个401.我怎样才能捕捉全球这个错误,并redirect到login页面/组件?

谢谢。


以下是我到目前为止:

// boot.ts

 import {Http, XHRBackend, RequestOptions} from 'angular2/http'; import {CustomHttp} from './customhttp'; bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS, new Provider(Http, { useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new CustomHttp(backend, defaultOptions), deps: [XHRBackend, RequestOptions] }) ]); 

// customhttp.ts

 import {Http, ConnectionBackend, Request, RequestOptions, RequestOptionsArgs, Response} from 'angular2/http'; import {Observable} from 'rxjs/Observable'; @Injectable() export class CustomHttp extends Http { constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) { super(backend, defaultOptions); } request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> { console.log('request...'); return super.request(url, options); } get(url: string, options?: RequestOptionsArgs): Observable<Response> { console.log('get...'); return super.get(url, options); } } 

我得到的错误消息是“backend.createConnection不是一个函数”

描述

我发现的最佳解决scheme是重写XHRBackend ,使得HTTP响应状态401403导致特定的操作。

如果您在Angular应用程序之外处理身份validation,则可以强制刷新当前页面,以便触发外部机制。 我在下面的实现中详细解释这个解决scheme

您也可以转发到您的应用程序内的组件,使您的Angular应用程序不重新加载。

履行

angular2.3.3前

以下实现适用于Angular 2.2.x FINALRxJS 5.0.0-beta.12

如果返回HTTP代码401或403,它将redirect到当前页面(加上一个参数来获取唯一的URL并避免caching)。

 import { Request, XHRBackend, BrowserXhr, ResponseOptions, XSRFStrategy, Response } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/catch'; import 'rxjs/add/observable/throw'; export class AuthenticationConnectionBackend extends XHRBackend { constructor(_browserXhr: BrowserXhr, _baseResponseOptions: ResponseOptions, _xsrfStrategy: XSRFStrategy) { super(_browserXhr, _baseResponseOptions, _xsrfStrategy); } createConnection(request: Request) { let xhrConnection = super.createConnection(request); xhrConnection.response = xhrConnection.response.catch((error: Response) => { if ((error.status === 401 || error.status === 403) && (window.location.href.match(/\?/g) || []).length < 2) { console.log('The authentication session expires or the user is not authorised. Force refresh of the current page.'); window.location.href = window.location.href + '?' + new Date().getMilliseconds(); } return Observable.throw(error); }); return xhrConnection; } } 

与以下模块文件。

 import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpModule, XHRBackend } from '@angular/http'; import { AppComponent } from './app.component'; import { AuthenticationConnectionBackend } from './authenticated-connection.backend'; @NgModule({ bootstrap: [AppComponent], declarations: [ AppComponent, ], entryComponents: [AppComponent], imports: [ BrowserModule, CommonModule, HttpModule, ], providers: [ { provide: XHRBackend, useClass: AuthenticationConnectionBackend }, ], }) export class AppModule { } 

Angular> 2.3.0

由于@mrgoos,这是一个针对2.3.0+的angular色的简化解决scheme,这是由于angular度2.3.0的bug修复(参见https://github.com/angular/angular/issues/11606 )直接扩展了Http模块。

 import { Injectable } from '@angular/core'; import { Request, XHRBackend, RequestOptions, Response, Http, RequestOptionsArgs, Headers } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/catch'; import 'rxjs/add/observable/throw'; @Injectable() export class AuthenticatedHttpService extends Http { constructor(backend: XHRBackend, defaultOptions: RequestOptions) { super(backend, defaultOptions); } request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> { return super.request(url, options).catch((error: Response) => { if ((error.status === 401 || error.status === 403) && (window.location.href.match(/\?/g) || []).length < 2) { console.log('The authentication session expires or the user is not authorised. Force refresh of the current page.'); window.location.href = window.location.href + '?' + new Date().getMilliseconds(); } return Observable.throw(error); }); } } 

模块文件现在只包含以下提供程序。

 providers: [ { provide: Http, useClass: AuthenticatedHttpService } ] 

使用路由器和外部authentication服务的另一个解决scheme在@mrgoos的以下要点中有详细说明。

angular4.3+

随着HttpClient的引入,能够轻松拦截所有请求/响应。 HttpInterceptors的一般用法有很好的logging ,请参阅基本用法以及如何提供拦截器。 下面是一个可以处理401错误的HttpInterceptor的例子。

 import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http' import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/do'; @Injectable() export class ErrorInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(req).do(event => {}, err => { if (err instanceof HttpErrorResponse && err.status == 401) { // handle 401 errors } }); } } 

你从每个请求方法得到的Observable<Response>的types是Observable<Response>Response对象具有status属性,该属性将保存服务器返回该代码的401 IF。 所以你可能想要在映射或转换之前检索它。

如果你想避免在每个调用中执行这个function,你可能需要扩展Angular 2的Http类,并注入你自己的实现,它调用常规Httpfunction的父类( super ),然后在返回对象之前处理401错误。

看到:

https://angular.io/docs/ts/latest/api/http/index/Response-class.html

从Angular> = 2.3.0,你可以覆盖HTTP模块并注入你的服务。 在版本2.3.0之前,由于核心错误,您无法使用注入的服务。

我已经创build了一个要点来展示它是如何完成的。

为了避免由像“路由器”这样的服务被注入Http派生类而导致的循环引用问题,必须使用后构造函数Injector方法。 以下代码是每次REST API返回“Token_Expired”时redirect到Login路由的Http服务的工作实现。 请注意,它可以用作常规Http的替代品,因此不需要更改应用程序已有组件或服务中的任何内容。

app.module.ts

  providers: [ {provide: Http, useClass: ExtendedHttpService }, AuthService, PartService, AuthGuard ], 

angular4.3+

完成吉尔伯特·阿里纳斯·匕首回答:

如果你需要的是拦截任何错误,对它进行一些处理并将其向前传递(而不是仅仅为.do添加一个副作用),你可以使用HttpClient及其拦截器来做类似的事情:

 import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/catch'; @Injectable() export class ErrorInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // install an error handler return next.handle(req).catch((err: HttpErrorResponse) => { console.log(err); if (err.error instanceof Error) { // A client-side or network error occurred. Handle it accordingly. console.log('An error occurred:', err.error.message); } else { // The backend returned an unsuccessful response code. // The response body may contain clues as to what went wrong, console.log(`Backend returned code ${err.status}, body was: ${err.error}`); } return Observable.throw(new Error('Your custom error')); }); } }