Повторить неудачный вызов API для другого источника в Angular - PullRequest
0 голосов
/ 08 сентября 2018

Я хочу реализовать логику повторения вызова 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 не вызывается и повторяется n раз с одним и тем же старым URL.

Таким образом, вопрос заключается в том, как реализовать общую повторную попытку для другой логики API, если текущий API станет недоступным.

Если перефразировать вопрос к более распространенному случаю, это похоже на то, как вызвать конечную точку HTTP с другими параметрами, если текущая ошибка не удалась.

1 Ответ

0 голосов
/ 12 сентября 2018
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, поэтому я жестко закодировал тип перед возвратом.
...