我现在想实现以下:我使用Oauth2身份验证与刷新令牌。我想构建一个api服务,所有其他服务将使用,并将透明地处理刷新令牌时返回401错误。因此,在401的情况下,我首先从OAuth2端点提取新的令牌,然后使用新的令牌重试我的请求。下面是代码工作正常,与promises:
request(url: string,request: RequestOptionsArgs): Promise<Response> { var me = this; request.headers = request.headers || new Headers(); var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://'); if (isSecureCall === true) { me.authService.setAuthorizationHeader(request.headers); } request.headers.append('Content-Type','application/json'); request.headers.append('Accept','application/json'); return this.http.request(url,request).toPromise() .catch(initialError => { if (initialError && initialError.status === 401 && isSecureCall === true) { // token might be expired,try to refresh token. return me.authService.refreshAuthentication().then((authenticationResult:AuthenticationResult) => { if (authenticationResult.IsAuthenticated == true) { // retry with new token me.authService.setAuthorizationHeader(request.headers); return this.http.request(url,request).toPromise(); } return <any>Promise.reject(initialError); }); } else { return <any>Promise.reject(initialError); } }); }
在上面的代码中,authService.refreshAuthentication()将获取新令牌并将其存储在localStorage中。 authService.setAuthorizationHeader将“授权”标头设置为以前更新的标记。如果你看看catch方法,你会看到它返回一个promise(对于刷新令牌),在它的轮到最终将返回另一个promise(对于实际的第二次尝试的请求)。
我试图这样做,而不诉诸承诺:
request(url: string,request: RequestOptionsArgs): Observable<Response> { var me = this; request.headers = request.headers || new Headers(); var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://'); if (isSecureCall === true) { me.authService.setAuthorizationHeader(request.headers); } request.headers.append('Content-Type',request) .catch(initialError => { if (initialError && initialError.status === 401 && isSecureCall === true) { // token might be expired,try to refresh token return me.authService.refreshAuthenticationObservable().map((authenticationResult:AuthenticationResult) => { if (authenticationResult.IsAuthenticated == true) { // retry with new token me.authService.setAuthorizationHeader(request.headers); return this.http.request(url,request); } return Observable.throw(initialError); }); } else { return Observable.throw(initialError); } }); }
上面的代码没有做我期望的:在200响应的情况下,它正确地返回响应。然而,如果它捕获401,它将成功检索新令牌,但订阅wil最终检索一个observable而不是响应。我猜这是未执行的Observable,应该做重试。
我意识到,将承诺的工作方式转换到rxjs库可能不是最好的方式去,但我没有能够抓住“一切都是流”的事情。我已经尝试了一些其他解决方案涉及flatmap,retryWhen等…但没有得到远,所以一些帮助是赞赏。
catch操作符期望您将返回一个Observable,它将连接到失败的Observable的结尾,以使下游Observer不知道差异。
在非401情况下,您通过返回一个重新抛出初始错误的Observable来正确地执行此操作。但是在刷新情况下,你返回一个Observable,产生更多的Observable,而不是单个值。
我建议你更改刷新逻辑为:
return me.authService .refreshAuthenticationObservable() //Use flatMap instead of map .flatMap((authenticationResult:AuthenticationResult) => { if (authenticationResult.IsAuthenticated == true) { // retry with new token me.authService.setAuthorizationHeader(request.headers); return this.http.request(url,request); } return Observable.throw(initialError); });
flatMap将中间Observables转换为单个流。