2

我正在调用 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 失败,我可以让 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");
     }));
    }

我看到几个例子,因为他们使用管道并添加 retryWhen() 来实现这一点。但是由于我对 Angular 很陌生,所以我无法找到一种方法来做到这一点。

有人可以帮忙吗?

4

2 回答 2

4

您可以使用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");
    })
  );
}

演示:https ://stackblitz.com/edit/angular-qyxpds

限制重试

这样做的危险是您将执行连续请求,直到服务器不返回 500 或 502。在实际应用程序中,您可能希望限制重试,并且可能会在其中放置某种延迟以避免淹没您的服务器与请求。

为此,您可以使用take(n)which 将您的请求限制为n失败的尝试。这对您不起作用,因为take会阻止 observable 继续前进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");
    })
  );
}

演示:https ://stackblitz.com/edit/angular-ud1t7c

于 2020-02-27T10:26:54.907 回答
1

做这个功能

           delayedRetry(delayMs:number,maxRetry:number){

            let retries=maxRetry;

            return (src:Observable<any>)=>
            src.pipe(
           retryWhen((errors:Observable<any>)=>errors.pipe(
           delay(delayMs),
           mergeMap(error =>retries -- > 0 ? 
           of(error):throwError("Retrying..."))
     ))
   )
  
}

在 api url 中调用它

    return this.http.get<RouteData[]>(url).pipe(
    this.delayedRetry(10000,4),
    catchError(err => {
   console.error(err);
    return EMPTY;
    }),
  shareReplay()
  );
于 2021-01-07T18:03:36.060 回答