因为我从angular2开始,我设置了我的服务来返回T的Observable.在服务中,我会调用map(),使用这些服务的组件只会使用subscribe()来等待响应.对于这些简单的场景,我真的不需要深入研究rxjs,所以一切都很好.

我现在希望实现以下目标:我正在使用带刷新令牌的OAuth2身份验证.我想构建一个所有其他服务都将使用的API服务,该服务将在返回401错误时透明地处理刷新令牌.因此,在401的情况下,我首先从OAuth2端点获取一个新令牌,然后使用新令牌重试我的请求.以下是运行良好的代码,并promise :

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中.授权服务.setAuthorizationHeader将"Authorization"头设置为以前更新的令牌.如果查看catch方法,您将看到它返回一个promise (对于刷新令牌),该promise 最终将返回另一个promise (对于请求的实际第二次try ).

我试图做到这一点,但没有诉诸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', '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,它将成功地检索到新的令牌,但是subscribe将最终检索到一个可观察的而不是响应.我猜这是应该进行重试的未执行的可观察对象.

我意识到,将promise的工作方式翻译到rxjs库中可能不是最好的方式,但我还没能理解"一切都是流"这一点.我try 了其他一些解决方案,包括flatmap、retryWhen等...但没走多远,所以我们很感激你的帮助.

推荐答案

通过快速查看您的代码,我想说您的问题似乎是您没有将refresh服务返回的Observable平坦化.

catch操作符期望您返回一个Observable,它将连接到失败的可观测数据的末尾,以便下游Observer不知道差异.

在非401的情况下,通过返回一个可观测值来重现初始错误,您可以正确地执行此操作.但是,在刷新的情况下,返回的是Observable,生成的是more Observables,而不是单个值.

我建议您将刷新逻辑更改为:

    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转化为单一流.

Angular相关问答推荐

Angular 16路卫单元测试可观察到的SpyObj属性

如何在自定义验证器中获取所有表单控件(字段组动态创建)

@ngrx/调度时未定义存储操作

如何在Primeng中实现自定义排序

Angular 单调服务已多次创建

Angular 17+独立配置中main.ts bootstrap 调用的最佳实践

如果我只在组件中使用某个主题,是否需要取消订阅该主题?

出错后可观察到的重用

合并Cypress和Karma的代码覆盖率报告JSON文件不会产生正确的结果

如何在 Angular RxJS 中替换订阅中的 subscrib?

如何处理后端删除元素后元素列表的刷新

Angular 为什么延迟加载返回空路径?

ngrx 效果等到从初始值以外的其他状态收到响应

Angular 13 - 何时创建嵌入式视图?

从 angular 11 升级到 angular 12 后,我的项目没有使用优化参数进行编译

使用 Jasmine 和 Karma 对 Angular 进行单元测试,错误:无法绑定到xxx,因为它不是xxxxxx的已知属性.

Angular Material 模态对话框周围的空白

Angular2material对话框自动关闭

升级到 angular-6.x 会给出Uncaught ReferenceError: global is not defined

Angular 4 - 获取输入值