Как повторить попытку вызова API после подписки в Angular 2+ - PullRequest
2 голосов
/ 27 февраля 2020

Я звоню API, чтобы получить случайные детали. Проблема в том, что иногда я получаю ошибку 502 как плохой шлюз, и она также может сломаться из-за плохого сетевого соединения. Ниже приведен мой код вызова API

// COMPONENT API CALL SUBSCRIBE
     this._service.post('randomAPI/getdetails', filters).subscribe((response: any) => {
     this.itemList = response;    
   });

// SHARED SERVICE
     post<T>(url: string, body: any): Observable<T> {
     return this.httpClient.post<T>(url, body);
   } 

Всякий раз, когда я получаю 500 или 502 ошибку сервера, с помощью перехватчика я перенаправляю на страницу ошибки, чтобы уведомить пользователя о проблеме с сервером.

Вместо этого Могу ли я заставить API попробовать еще раз на уровне компонента или на уровне перехватчика, если он не работает, и затем перейти на страницу ошибки?

// INTERCEPTOR
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(catchError(error => {
        if (error.status === 401 || error.status === 400 || error.status === 403) {
            this.router.navigateByUrl('abort-access', { replaceUrl: true });
        } else if (error.status === 500 || error.status === 502) {
                this.router.navigateByUrl('server-error', { replaceUrl: true });
        }
        return throwError("error occured");
     }));
    }

Я видел несколько примеров, поскольку они используют pipe и добавляют retryWhen () для достижения этой цели. Но так как я очень плохо знаком с angular, я не могу найти способ сделать это.

Может кто-нибудь помочь, пожалуйста?

1 Ответ

2 голосов
/ 27 февраля 2020

Вы можете использовать оператор retryWhen. Принцип, лежащий в основе этого, заключается в том, что вы выдаете ошибку, когда не хотите повторить попытку.

retryWhen фактически является фантастическим catchError, который автоматически повторяет попытку, если не будет выдана ошибка .

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  return next.handle(request).pipe(
    // retryWhen operator should come before catchError operator as it is more specific
    retryWhen(errors => errors.pipe(
      // inside the retryWhen, use a tap operator to throw an error 
      // if you don't want to retry
      tap(error => {
        if (error.status !== 500 && error.status !== 502) {
          throw error;
        }
      })
    )),

    // now catch all other errors
    catchError(error => {     
      if (error.status === 401 || error.status === 400 || error.status === 403) {
        this.router.navigateByUrl('abort-access', { replaceUrl: true });
      }

      return throwError("error occured");
    })
  );
}

DEMO: https://stackblitz.com/edit/angular-qyxpds

Ограничение повторных попыток

Опасность заключается в том, что вы будете выполнять непрерывные запросы, пока сервер не выполнит не верните 500 или 502. В реальном приложении вы хотели бы ограничить количество повторных попыток и, возможно, добавить некоторую задержку, чтобы избежать переполнения вашего сервера запросами.

Чтобы сделать это, вы могли бы используйте take(n), что ограничит ваши запросы n неудачными попытками. Это не будет работать для вас, потому что take остановит наблюдаемое от перехода к catchError, и вы не сможете выполнять навигацию.

Вместо этого вы можете установить предел повторных попыток и бросить ошибка после достижения предела повторных попыток.

const retryLimit = 3;
let attempt = 0;

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  return next.handle(request).pipe(
    // retryWhen operator should come before catchError operator as it is more specific
    retryWhen(errors => errors.pipe(
      tap(error => {
        if (++attempt >= retryLimit || (error.status !== 500 && error.status !== 502)) {
          throw error;
        }
      })  
    )),

    // now catch all other errors
    catchError(error => {     
      if (error.status === 401 || error.status === 400 || error.status === 403) {
        this.router.navigateByUrl('abort-access', { replaceUrl: true });
      } else if (error.status === 500 || error.status === 502) {
        this.router.navigateByUrl('server-error', { replaceUrl: true });
        // do not return the error
        return empty();
      }

      return throwError("error occured");
    })
  );
}

DEMO: https://stackblitz.com/edit/angular-ud1t7c

...