javascript 以 6 角重试 HTTP 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51905963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 09:44:49  来源:igfitidea点击:

Retry HTTP requests in angular 6

javascriptangularhttprxjs

提问by Amir Khademi

I use an interceptor to show error messages on display based on the HTTP response for every request.

我使用拦截器根据每个请求的 HTTP 响应在显示器上显示错误消息。

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const customReq = request.clone({
        //headers: request.headers.set('app-language', 'en')
    });
    return next
        .handle(customReq).pipe(
            tap((ev: HttpEvent<any>) => {
                if (ev instanceof HttpResponse) {
                    // processing request
                }
            }),
            catchError(response => {
                if (response instanceof HttpErrorResponse) {
                    switch (response.status) {
                        case 0:
                            // server API connection error show
                            break;
                        case 403:
                            // error Token Invalid and Redirect to logout
                            break;
                        case 401:
                            // error Token Invalid and Redirect to logout
                            break;
                        case 502:
                            // Bad gateway error
                            break;
                        case 500:
                            // internal server error
                            break;
                    }
                }
                // return next.handle(request);
                return observableThrowError(response);
            })
        );
}

In my project, the web server could be unavailable for a second in some case and reply a 500 error code. I don't want my web app to show an error message immediately after receiving an error and I want it to retry the request for a couple of times with a delay like one second.

在我的项目中,Web 服务器在某些情况下可能会暂时不可用并回复 500 错误代码。我不希望我的 Web 应用程序在收到错误后立即显示错误消息,我希望它重试请求几次,延迟时间为一秒钟。

I already tried rxjs retry :

我已经尝试过 rxjs 重试:

...
.handle(customReq).pipe(retry(5),
...

it's not useful since it has no delay. based on this answer How to create an RXjs RetryWhen with delay and limit on tries

它没有用,因为它没有延迟。基于此答案 如何创建具有延迟和限制尝试的 RXjs RetryWhen

I tried retryWhen like:

我试过 retryWhen 喜欢:

.handle(customReq).pipe(
    tap((ev: HttpEvent<any>) => {
        if (ev instanceof HttpResponse) {
            console.log('###processing response', ev, this.location);
        }
    }),
    retryWhen(error => {
        return error
            .flatMap((error: any) => {
                if (error.status  == 400) {
                    return Observable.of(error.status).delay(1000);
                }
                if (error.status  == 0) {
                    return observableThrowError(error).delay(1000);
                }
                return observableThrowError(error);
            })
            .take(5)
            .concat(observableThrowError(error));
    }),

but it doesn't work as expected and it doens't go inside the if conditions.

但它没有按预期工作,并且不会进入 if 条件。

回答by m1ch4ls

There are several errors in your code:

您的代码中有几个错误:

  1. You are shadowing errorvariable - first it's an error stream and then it's an error object.
  2. Using observableThrowErrorwith delayhas no effect. Error will bypass every operator except those dealing with error.
  3. You are mixing "pipe(operator())" style operators and prototype style operators .operator().
  1. 您正在隐藏error变量 - 首先它是一个错误流,然后它是一个错误对象。
  2. 使用observableThrowErrorwithdelay没有效果。除了处理错误的操作符之外,错误将绕过所有操作符。
  3. 您正在混合“ pipe(operator())”样式运算符和原型样式运算符.operator()

I have suggested some changes:

我提出了一些改变:

.handle(customReq).pipe(
    tap((ev: HttpEvent<any>) => {
        if (ev instanceof HttpResponse) {
            console.log('###processing response', ev, this.location);
        }
    }),
    retryWhen(errors => errors
        .pipe(
            concatMap((error, count) => {
                if (count < 5 && (error.status == 400 || error.status == 0)) {
                    return Observable.of(error.status);
                }

                return observableThrowError(error);
            }),
            delay(1000)
        )
    ),

Main change is tracking error count through second argument of concatMapinstead of using takeopeator which is mainly useful for unsubscribing the observable and I think you want throw an error instead.

主要变化是通过第二个参数concatMap而不是使用take运算符来跟踪错误计数,这主要用于取消订阅可观察对象,我认为您想要抛出错误。

回答by Santiago Ceron

You could use retry operator, better to use it on an interceptor

您可以使用重试运算符,最好在拦截器上使用它

 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
        .pipe(retry(RETRY_NUMBER))

It will only retry when the request fails, then to identify errors you could throw an error and then catch it

它只会在请求失败时重试,然后为了识别错误,您可以抛出错误然后捕获它

.pipe(tap(event => {
            // tipo http
            if (event instanceof HttpResponse) {
                if (event.body && event.body.error === true && event.body.errorMessage) {
                    throw new Error(event.body.errorMessage);
                }
            }
        }))


.pipe(catchError((err) => {

      if (err.status === 0) {
          //do something
      }
      // other conditions ...

      return EMPTY;
}));

Don't forget t return EMPTYbecause the interceptor needs to return an observable

不要忘记 t returnEMPTY因为拦截器需要返回一个 observable