如何在RxJs中执行操作后重试observable?

时间:2017-08-02 16:18:22

标签: angular rxjs reactive-programming

我正在使用Angular 2和RxJS从我的API层检索数据。我有一个基本服务,我已经编写,以处理HTTP请求。以下代码在我的服务中执行此操作:

protected apiGet(uri: string): Observable<HttpMappedResponse<T>> {
    return this.mapAndHandle<T>(this.http.get(uri));
}

private mapAndHandle<T>(observable: Observable<Response>): Observable<HttpMappedResponse<T>> {
    const o = observable
        .map(res => {
            const data = res.json() || {};
            const mappedResponse = new HttpMappedResponse<T>(res, data);
            // Retrieve page information from HTTP headers
            mappedResponse.pageInfo = new PageInfo(res.headers);
            return mappedResponse;
        })
        .share();

    o.subscribe(null, e => this.errorService.handleHttpResponse(e));
    return o;
}

这很有效,并且在我拨打apiGet()时会向我提供所需的所有数据。我现在要做的是以下内容:

  • 发送HTTP请求
  • 如果返回带有特定字符串的401错误:
    • 将另一个请求发送到其他网址
    • 更新其他服务
    • 重试原始请求
    • (然后可能添加逻辑来处理可能的无限重试)
  • 否则
    • 重新抛出原始的http错误
  • 捕获错误并处理它

我尝试过使用.catch(),. retryWhen(),。map()和.flatMapTo()但没有成功 - 我最接近的是:

enum HttpVerb {
    Put,
    Post,
    Get,
    Delete
}

private mapAndHandle<T>(uri: string, type: HttpVerb): Observable<HttpMappedResponse<T>> {
    const restObservable = this.getRestObservable(uri, type);

    const o = restObservable
        .map(res => this.mapToHttpMappedResponse<T>(res))
        .catch((error, sourceObservable) => {
            const data = error.json() || {};

            if (error.status === 401 && data.Message === 'Token expired') {
                console.log('Retrying');
                // Refresh the expired auth token
                const refreshToken = this.authService.userData.RefreshToken;
                console.log(refreshToken);

                this.authService
                    .refresh(new RefreshRequestModel(refreshToken))
                    .subscribe(x => null);

                // Retry original
                return this
                    .getRestObservable(uri, type)
                    .map(x => this.mapToHttpMappedResponse(x));
            }
            throw error;
        })
        .share();

    o.subscribe(null, e => {
        this.errorService.handleHttpResponse(e);
    });
    return o;
}

private mapToHttpMappedResponse<T>(res: Response): HttpMappedResponse<T> {
    console.log('Attempt ' + Date.now());
    const data = res.json() || {};
    const mappedResponse = new HttpMappedResponse<T>(res, data);
    // Retrieve page information from HTTP headers
    mappedResponse.pageInfo = new PageInfo(res.headers);
    return mappedResponse;
}

但这并没有给我正确的结果。

我可以使用操作员来执行此操作吗? retryWhen()似乎是最合乎逻辑的,但我只是让自己陷入混乱的RxJs!

1 个答案:

答案 0 :(得分:1)

您遇到的一个问题是您没有连接电话。您的刷新调用应该链接到您的重试调用,他们应该利用flatMap或类似的东西将它保存在同一个可观察的流中。

请看my answer这个问题。

这就是你如何实现它的目的:

您需要从RxJS添加catch运算符。这是一个错误,你可以相应地处理它。

为了使刷新逻辑通过拦截器,您需要返回调用,函数也应该返回Observable。例如,修改上面的原始逻辑:

this.http.request(url, options)
        .map((res: Response) => res.json())
        .catch((error: any) => {
            const err = error.json();

            // Refresh JWT
            if (err.status === 401) {
                // refreshToken makes another HTTP call and returns an Observable.
                return this.refreshToken(...);
            }

            return Observable.throw(err);
        });

如果您希望能够重试原始请求,您可以执行的操作是传递原始请求数据,以便在成功刷新令牌后,您可以再次进行呼叫。例如,这就是refreshToken函数的外观:

refreshToken(url: string, options: RequestOptionsArgs, body: any, tokenData: any): Observable<any>
    return this.post(`${this.url}/token/refresh`, tokenData)
        .flatMap((res: any) => {
            // May want to use response data to set new tokens in your local storage or whatever here...

            // This is where I retry the original request
            return this.request(url, options, body);
        });
}