【发布时间】:2018-03-29 10:30:48
【问题描述】:
我想使用全局错误处理程序来处理访问令牌过期并使用刷新令牌刷新它们。我几乎似乎可行的解决方案如下:
从你的组件中,订阅一个 observable,如下所示:
this.myHttpService.getSomeData().subscribe(response => {
console.log('user logged in', response );
},
error => {
// optionally do something custom here.
console.log(error);
throw error;
// we could handle the error here, getting the new auth token and calling this again.
}
上面的错误处理程序不是必需的,因为我们可以在全局错误处理程序中处理错误,但您可能想在这里处理其他事情。
在我的 http 服务中,我有一个返回 observable 的函数,如下所示:
getSomeData(): Observable<any> {
return this.http.get(this.API_URL_BASE + '/api/activities/getsomeData, this.httpHelperMethodsService.defaultOptions).map((response: Response) => {
return response.json();
}).catch(this.handleError);
}
处理身份验证令牌过期的过程如下: 1.捕捉错误 2. 识别授权令牌已过期 3.调用以使用刷新令牌获取新的身份验证令牌 4. 重新运行源 observable
在服务中,实现处理错误函数,如下所示:
private handleError(error: any, originalObservable: any) {
let errorHelper = { 'error': error, 'observable': originalObservable};
return Observable.throw(errorHelper);
}
然后您可以创建一个全局错误处理程序,如下所示:
@Injectable()
export class ApplicationErrorHandler extends ErrorHandler {
constructor(private injector: Injector) {
super(false);
}
handleError(error: any): void {
const refreshTokenService = this.injector.get(RefreshTokenService);
...
else if(error.statusText == "Unauthorized"){
// check if token is expired. This will involve looking in local storage and comparing expiry time to current time.
if(isTokenExpired){
refreshTokenService.refreshToken(refreshToken).subscribe(response => {
// set refresh token
error.observable().subscribe(response => {
// return response.
}, error => {
// return error.
})
}
}
}
}
}
使用http-interceptor,配置如下:
import {Injectable} from "@angular/core";
import { ConnectionBackend, RequestOptions, Request, RequestOptionsArgs, Response, Http, Headers} from "@angular/http";
import {Observable} from "rxjs/Rx";
@Injectable()
export class InterceptedHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
return super.request(url, options);
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
return super.get(url, this.getRequestOptionArgs(options));
}
post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
return super.post(url, body, this.getRequestOptionArgs(options));
}
put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
return super.put(url, body, this.getRequestOptionArgs(options));
}
delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
return super.delete(url, this.getRequestOptionArgs(options));
}
private getRequestOptionArgs(options?: RequestOptionsArgs) : RequestOptionsArgs {
return this.getOptionsWithAccessToken();
}
getOptionsWithAccessToken() {
let accessToken = localStorage.getItem('access_token');
let accessTokenJson = <any>JSON.parse(accessToken);
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Authorization', accessTokenJson);
let options = new RequestOptions({ headers: headers });
console.log('options updated', options);
return options;
}
}
然而,当再次订阅原始 observable 时,不会调用 http 拦截器,而是得到另一个 401。根据日志语句console.log('options updated', options);,我可以看到这个拦截器在所有其他 http 调用之前被调用。订阅原始observable时好像没有运行http拦截器,是这样吗?
有没有办法更新这个 observable 上的选项以使用新的身份验证令牌?也许我可以在可观察对象上调用其他方法之一而不是订阅?或者这是不可能的?我能想到的唯一选择是在每个 http 调用中捕获错误,但我有大约一百个,所以宁愿不这样做。
【问题讨论】:
-
谢谢。是的,这听起来像是解决方案。如果在拦截器中设置了身份验证令牌,那么它将按预期设置。我现在要回家了,明天试试这个。
-
@JBNizet 我已更新问题以包括我尝试使用 http 拦截器,但不幸的是这不起作用。