首页 > 解决方案 > 重试失败的 API 调用到 Angular 中的另一个源

问题描述

如果对当前源的调用失败,我想实现一个重试对另一个源的 API 调用的逻辑。我希望它在服务层中处理,而不是在每个组件中实现这个逻辑。

例如,我在端点服务中有这样的功能

 getAll(): Observable<IssueListItem[]> {
        let endpointUrl = `${this.apiConnectionService.getApiUrl()}api/Issues`;
        return this.http.get<IssueListItem[]>(endpointUrl, { headers: this.dataService.requestHeaders })
            .pipe(retryOtherApi(this.apiConnectionService),
                catchError(error => this.handleError(error)));
    }

此函数的使用者如下所示:

ngOnInit() {
    this.issuesEndpointService.getAll()
        .subscribe(_ => this.issues = _);
}

我希望它对重试逻辑一无所知。

因此,我尝试创建一个操作员“retryOtherApi”,在其中将原点切换到另一个原点。

export function retryOtherApi(apiConnectionService: ApiConnectionService) {
    const maxRetry = apiConnectionService.apiOriginsCount;

    return (src: Observable<any>) => src.pipe(
        retryWhen(_ => {
            return interval().pipe(
                flatMap(count => {
                    console.log('switch to: ' + apiConnectionService.getApiUrl())
                    apiConnectionService.switchToOther();
                    return count === maxRetry ? throwError("Giving up") : of(count);
                })
            );
        })
    );
}

切换工作,但不幸的是,整个 getAll 函数没有被调用,它使用相同的旧 URL 重试 n 次。

所以问题是如果当前 API 变得不可用,如何实现对其他 API 逻辑的公共重试。

如果将问题重新表述为更常见的情况,它就像如果当前一个参数失败,如何用其他参数调用 HTTP 端点。

标签: javascriptangularhttprxjsobservable

解决方案


public getAll(): Observable<IssueListItem[]> {
    let call = () => {
        let endpointUrl = `${this.apiConnectionService.getApiUrl()}api/Issues`;
        return this.http.get<IssueListItem[]>(endpointUrl, { headers: this.dataService.requestHeaders })
    };

    return <Observable<IssueListItem[]>>call()
        .pipe(
            retryOtherApi(this.apiConnectionService, () => call()),
            catchError(error => this.handleError(error))
        );
}

这是一个重试运算符:

export function retryOtherApi(apiConnectionService: ApiConnectionService, recall: () => Observable<any>) {
    const maxRetry = apiConnectionService.apiOriginsCount;
    let count = 0;

    let retryLogic = (src: Observable<any>) => src.pipe(
        catchError(e => {
            if (e.status === 0) {
                apiConnectionService.switchToOther();
                console.log('switch to: ' + apiConnectionService.getApiUrl())
                return count++ === maxRetry ? throwError(e) : retryLogic(recall());
            } else {
                return throwError(e);
            }
        }));

    return retryLogic;
}

它有效,但是:

  1. 现在我需要为其他线程实现逻辑,不要同时切换 api。
  2. TS 类型转换有一些问题,这就是我在返回之前对类型进行硬编码的原因。

推荐阅读