使用rxjs处理刷新标记

使用rxjs处理刷新标记

问题描述:

由于我已经开始使用angular2,我已经设置了我的服务来返回T的Observable。在服务中,我将使用map()调用,并且使用这些服务的组件仅使用subscribe()等待回应。对于这些简单的场景,我并不需要深入研究rxjs,因此一切正常。使用rxjs处理刷新标记

我现在想要实现以下功能:我正在使用带有刷新令牌的Oauth2身份验证。我想构建一个所有其他服务将使用的api服务,并且在返回401错误时它将透明地处理刷新令牌。因此,在401的情况下,我首先从OAuth2端点获取新令牌,然后使用新令牌重试我的请求。下面是正常工作,与承诺的代码:)

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方法,你会发现它会返回一个承诺(对于刷新令牌),最终会返回另一个承诺(对于请求的第二次尝试)。

我试图做到这一点,而不诉诸承诺:

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', 'application/json'); 
    request.headers.append('Accept', 'application/json'); 

    return this.http.request(url, 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,它将成功检索新的令牌,但订阅将最终检索observable而不是响应。我猜这是未执行的Observable,应该重试。

我意识到将工作的承诺方式转换到rxjs库可能不是最好的方法,但我还没有能够掌握“一切都是流”的东西。我尝试了一些其他解决方案,涉及flatmap,retryWhen等......但没有太多,所以一些帮助表示赞赏。

从快速查看您的代码,我会说你的问题似乎是,你没有扁平从refresh服务返回Observable

catch运营商期望,你会返回一个Observable它将串联到失败的可观测的结束,使下游Observer不知道其中的差别。

在非401的情况下,您通过返回反映初始错误的Observable正确地执行此操作。但是在更新情况下,您要退回Observable生产更多Observables而不是单个值。

我建议你更改刷新逻辑是:

​​

flatMap将中间Observables转换成一个单一的数据流。

+0

哈,这工作:)我试过使用flatMap方法,但显然不是以正确的方式。 Thanx Paul! Ps:我在rxjs中发现的东西很杂乱,就是他们倾向于将这些方法隐藏在具有不同名称的文件中。为了使flatMap方法起作用,我必须导入“mergeMap”文件... – Davy

+0

@Davy我可以问你正在使用哪个版本?如果是来自ReactiveX的项目,它目前仍处于测试阶段,所以在完成所有功能时文档仍然有点欠缺。 – paulpdaniels

+0

这一个:https://github.com/ReactiveX/RxJS(“rxjs”npm包)。 – Davy

在RxJs的最新版本中,flatMap运营商已更名为mergeMap