我是使用Angular和Rxjs的新手。
基本上,应用程序使用Api并需要提供有效的身份验证令牌。 此令牌是短暂的,如果它已过期,则可以刷新。
我编写了一个特定的http服务,它为每个请求添加了正确的标头,如果验证失败,则尝试刷新令牌并重试。
以下是我实施它的方式:
export class HttpAService extends Http {
constructor(
backEnd: XHRBackend,
options: RequestOptions,
private authenticationService: AuthenticationService
) {
super(backEnd, options);
// get an authentication token from an authentication service
let token = authenticationService.token;
options.headers.set('Authorization', `Bearer ${token}`);
}
private request1(
url: string | Request,
options?: RequestOptionsArgs
): Observable<Response> {
// get a token from an authentication service
let token = this.authenticationService.token;
// add it to the request headers
if (typeof url === 'string') {
if (!options) {
options = { headers: new Headers };
}
options.headers.set('Authorization', `Bearer ${token}`);
} else {
url.headers.set('Authorization', `Bearer ${token}`);
}
// make the request
return super.request(url, options);
}
public request(
url: string | Request,
options?: RequestOptionsArgs
): Observable<Response> {
// init retry flag
let retried: boolean = false;
// make the request and retry one time in case of failure
const obs = Observable.defer(() => this.request1(url, options));
return obs.retryWhen(attempt => {
// request failed
return attempt.flatMap(res => {
// if it's the first round and an authentication error try to refresh token
if ((!retried) && (500 === res.status) && ('Unauthenticated.' == res.json().message)) {
retried = true;
// refresh attempts to create a new token that can be retrieved using authenticationService.getToken.
return this.authenticationService.refresh();
}
return Observable.throw(res);
})
})
}
}
我遇到的问题是让重试请求使用新令牌(换句话说,在重试期间重新运行request1函数)。看起来defer(...)就是这个伎俩。
我想知道这个实现是否正确,是否有更优雅的方式来实现这种行为。
感谢您的回复。
答案 0 :(得分:0)
对于那些基于此问题的人,我认为推荐的运营商是retryWhen
。这是一个例子。
get(url:string): Observable<Object> {
return this.httpClient.get(url, {headers: this.authHeaders}).pipe(
retryWhen(errors => errors.pipe(
switchMap((e:HttpErrorResponse) => {
if (e.status === 403) return this.refreshAuthToken();
console.warn("HTTP GET error", url, e); //like 500 internal server error
throw e;
})
))
)
}
但是有一个缺陷。虽然refreshAuthToken
完美地工作,但原始的HTTP observable仍然停留在未经过身份验证的令牌上。不知道怎么解决这个问题。
所以现在我使用一个有效的解决方案,但它以递归方式调用我的get
方法,而不是使用运算符。
get(url:string): Observable<Object> {
return this.httpClient.get(url, {headers: this.authHeaders}).pipe(
catchError((e:HttpErrorResponse) => {
if (e.status === 403) {
return this.refreshAuthToken().pipe(
switchMap(success => success? this.get(url) : of(null)) //recursive call not cool :(
)
}
console.warn("HTTP GET error", url, e); //like 500 internal server error
return of(null);
})
)
}
希望有人提供更好的答案。