如何使用角度 4.3 中的新 httpClient 处理未经授权的请求(状态为 401 或 403)
Posted
技术标签:
【中文标题】如何使用角度 4.3 中的新 httpClient 处理未经授权的请求(状态为 401 或 403)【英文标题】:How to handle unauthorized requests(status with 401 or 403) with new httpClient in angular 4.3 【发布时间】:2018-02-11 12:32:37 【问题描述】:我有一个auth-interceptor.service.ts
来处理请求
import Injectable from '@angular/core';
import HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest from '@angular/common/http';
import Observable from 'rxjs/Observable';
import Cookie from './cookie.service';
import Router from '@angular/router';
@Injectable()
export class AuthInterceptor implements HttpInterceptor
constructor(private router: Router)
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
// Clone the request to add the new header.
const authReq = req.clone(headers: req.headers.set(Cookie.tokenKey, Cookie.getToken()));
// Pass on the cloned request instead of the original request.
return next.handle(authReq).catch(this.handleError);
private handleError(err: HttpErrorResponse): Observable<any>
console.log(err);
if (err.status === 401 || err.status === 403)
Cookie.deleteUser();
this.router.navigateByUrl(`/login`);
return Observable.of(err.message);
// handle your auth error or rethrow
return Observable.throw(err);
但我收到以下错误。没有任何事情发生,例如它没有删除 cookie 或没有导航到登录页面 任何帮助或建议将不胜感激。
【问题讨论】:
HttpClient 支持拦截器。您应该使用它来透明地处理错误,而不是强制所有代码直接使用 ApiService 而不是 HttpClient。 @JBNizet 我也有一个身份验证拦截器。我已经更新了帖子。那是处理此类请求的地方吗?有示例代码吗?? 如果您使用您的方法,您将需要包装整个 http 服务。我建议使用拦截器或扩展 http 服务并仅覆盖请求方法。 @bryan60 我还有一个拦截器来为每个请求添加标头。我不确定如何处理拦截器中任何请求的响应。我已经更新了帖子 在答案中添加了一个示例 【参考方案1】:你应该使用你的拦截器并像这样处理它:
@Injectable()
export class AuthInterceptor implements HttpInterceptor
constructor(private router: Router)
private handleAuthError(err: HttpErrorResponse): Observable<any>
//handle your auth error or rethrow
if (err.status === 401 || err.status === 403)
//navigate /delete cookies or whatever
this.router.navigateByUrl(`/login`);
// if you've caught / handled the error, you don't want to rethrow it unless you also want downstream consumers to have to handle it as well.
return of(err.message); // or EMPTY may be appropriate here
return throwError(err);
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
// Clone the request to add the new header.
const authReq = req.clone(headers: req.headers.set(Cookie.tokenKey, Cookie.getToken()));
// catch the error, make specific functions for catching specific errors and you can chain through them with more catch operators
return next.handle(authReq).pipe(catchError(x=> this.handleAuthError(x))); //here use an arrow function, otherwise you may get "Cannot read property 'navigate' of undefined" on angular 4.4.2/net core 2/webpack 2.70
不需要 http 服务包装器。
要使用路由器,您需要一个工厂供应商,例如:
providers: [
provide: HTTP_INTERCEPTORS,
useFactory: function(router: Router)
return new AuthInterceptor(router);
,
multi: true,
deps: [Router]
,
.... other providers ...
]
您在哪里提供拦截器(可能是 app.module)。不要使用箭头函数。当您尝试为 prod 构建时,工厂函数不支持它们。
工作笨蛋:https://plnkr.co/edit/UxOEqhEHX1tCDVPDy488?p=preview
【讨论】:
非常感谢您的回复并添加到我的代码中。我遇到了一些奇怪的错误。我不确定为什么this.router
未定义。我已经用回复更新了帖子。感谢您的帮助。
你需要一个工厂函数来提供一个 httpinterceptor 并注入路由器...见这个 github gist.github.com/mrgoos/45ab013c2c044691b82d250a7df71e4c
我认为是在拦截器中使用路由器,http拦截器在路由器之前注册,因此由于angular的分层注入器,没有它就无法注入
是的,我也遇到了同样的错误。看起来我需要你提供的 gist 链接中的工厂函数。
我添加了一个工厂提供者的例子,答案是 github 在工厂函数中使用了箭头函数,你不应该这样做。您的构建将失败。【参考方案2】:
根据@bryan60 的建议,我对他的解决方案做了一些更改
在 app.module.ts 中:
providers: [
provide: HTTP_INTERCEPTORS,
useFactory: function(injector: Injector)
return new AuthInterceptor(injector);
,
multi: true,
deps: [Injector]
,
.... other providers ...
]
在 auth-interceptor.service.ts 中:
import Injectable, Injector from '@angular/core';
import HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest from '@angular/common/http';
import Observable from 'rxjs/Observable';
import Cookie from './cookie.service';
import Router from '@angular/router';
import UserService from './user.service';
import ToasterService from '../toaster/toaster.service';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
@Injectable()
export class AuthInterceptor implements HttpInterceptor
constructor(private injector: Injector)
private handleError(err: HttpErrorResponse): Observable<any>
let errorMsg;
if (err.error instanceof Error)
// A client-side or network error occurred. Handle it accordingly.
errorMsg = `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,
errorMsg = `Backend returned code $err.status, body was: $err.error`;
if (err.status === 404 || err.status === 403)
this.injector.get(UserService).purgeAuth();
this.injector.get(ToasterService).showError(`Unauthorized`, errorMsg);
this.injector.get(Router).navigateByUrl(`/login`);
console.error(errorMsg);
return Observable.throw(errorMsg);
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
// Clone the request to add the new header.
const authReq = req.clone(headers: req.headers.set(Cookie.tokenKey, Cookie.getToken()));
// Pass on the cloned request instead of the original request.
return next.handle(authReq).catch(err => this.handleError(err));
如果您在构建时使用 AOT,请尝试:
export function authInterceptorFactory(injector: Injector)
return new AuthInterceptor(injector);
providers: [
provide: HTTP_INTERCEPTORS,
useFactory: authInterceptorFactory,
multi: true,
deps: [Injector]
,
.... other providers ...
]
【讨论】:
为什么使用注入器而不是显式注入?您的服务现在有 3 个隐藏的依赖项。如果你只在一个地方做这没什么大不了的,但是如果你把它变成一种习惯,你的项目可能会变得难以修改和维护。 是的,谢谢你的信息。这是我唯一这样做的地方,它直接导入到应用程序模块中。我确保没有其他模块导入此拦截器。【参考方案3】:上面@bryan60 的回答很好用,如果有人像我一样遇到问题并发现下面一行中的错误
return next.handle(authReq).catch(x=> this.handleAuthError(x));
使用 do() 处理错误(如果遇到 catch() 问题)
导入文件:
import 'rxjs/add/operator/do';
处理错误:
return next.handle(authReq)
.do(
success => /*todo*/,
err => this.handleAuthError(authReq)
);
handleAuthError(err: any)
if(err.status === 401 || err.status === 403)
this.storageService.clear();
window.location.href = '/home';
我希望这对某人有所帮助。
【讨论】:
如何注入 storageService?以上是关于如何使用角度 4.3 中的新 httpClient 处理未经授权的请求(状态为 401 或 403)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Apache HttpClient 4.3 处理 Cookie
如何在测试中模拟 Angular 4.3 httpClient 的错误响应