【问题标题】:Angular 5 Interceptor - Only call second interceptor after failed retry on first interceptorAngular 5拦截器-仅在第一个拦截器重试失败后才调用第二个拦截器
【发布时间】:2021-04-02 00:50:25
【问题描述】:

我正在构建一个 Angular 5 应用程序,其中有 2 个拦截器:

  • 一个重试失败的504个请求
  • 另一个向用户显示有关失败请求的错误消息

我希望第二个拦截器仅在错误不是 504 或错误为 504 并且已被第一个拦截器重试时才被调用。

我创建了一个包含两个拦截器的示例:https://stackblitz.com/edit/angular-interceptors-ckbvsb

希望能得到一些帮助!

谢谢

更新:

感谢大家的帮助!我最终是这样实现的:

@Injectable()
export class I1 implements HttpInterceptor {
 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
 console.log('intercepted I1');

 return next.handle(req)
  .retryWhen(error => {
    return error
      .mergeMap((err, i) => {
        if (i > 0) return Observable.throw(err)
        if (err instanceof HttpErrorResponse && err.status === 504) { // timeout
          return Observable.of(err).delay(5000);
        } else {
          return Observable.throw({ error: 'No retry', cause: err })
        }
      })
  });
 }
}

【问题讨论】:

    标签: angular rxjs interceptor


    【解决方案1】:

    您的问题是您提供两个拦截器的顺序。您首先提供 I1,然后提供 I2。这意味着请求将流经 I1 → I2,但 响应 将流经 I2,然后才流经 I1。所以只需切换顺序:

    @NgModule({
        imports: [BrowserModule, HttpClientModule],
        declarations: [AppComponent],
        providers: [
            {
                provide: HTTP_INTERCEPTORS,
                useClass: I2,               // <-- I2 first
                multi: true
            },
            {
                provide: HTTP_INTERCEPTORS,
                useClass: I1,               // <-- And only then I1
                multi: true
            }
        ],
        bootstrap: [AppComponent]
    })
    export class AppModule {
    }
    

    【讨论】:

    • 请再次阅读我的回答。这与提供它们的顺序有关。
    • 检查 OP stackblitz,它们已经按此顺序排列。这不是这里的问题。
    • 我不知道我们是否正在查看不同的链接,但我在发布的 Stackblitz 上看到的是 app.module.ts 中 I1 是第一,I2 是第二。
    • @IngoBürk 更改顺序似乎已经解决了问题。我不知道这种处理响应的方式。但是重试后如何将 HttpError 传递给 I2?这样它只传递消息,但我也想在 I2 上获取 HttpErrorResponse。谢谢
    • @HobojoeBr,见my answer
    【解决方案2】:

    这是一种实现方式 - 将其应用于您的代码:

    // an observable producer that produces an error
    const producer = (obs) => {
        obs.next(0);
        obs.error(new Error('504'));
    };
    
    const i1 = Observable.create(producer).pipe(
        retryWhen((errors) => {
            return errors.pipe(
                scan((a, v, i) => {
                    a.unshift(v);
                    return a;
                }, []),
                map((v) => {
                    if (!v[0].message.includes('504') || v.length > 1) {
                        throw v[0];
                    } else {
                        return v[0].message;
                    }
                }),
                delay(2000)
            )
        })
    );
    

    您可以了解有关创建 observables 的更多信息here

    【讨论】:

    • 谢谢!您的解决方案有效,但我无法添加延迟,可能是因为我是 RXJS 的新手。但我非常感谢您的帮助!
    • @HobojoeBr,只需添加delay 运算符。我已经更新了我的代码。您在mergeMap 中使用index 的解决方案可能比我的更容易理解:)
    • 太好了,我在使用 RxJs 方面还有很长的路要走!谢谢
    • @AngularInDepth.com:您介意看看我在以下question 中遇到的几乎相同的问题吗?我想实现您的解决方案,但无法弄清楚如何在我的代码中实现它。谢谢
    • @k.vincent,有时间我去看看
    【解决方案3】:

    我不确定这是处理此类事情的正确方法,拦截器用于检查请求,而不是响应,它们不关心返回的响应,因为您在看到响应之前使用 .next 传递请求。

    似乎更适合在检查响应然后向其他资源发出另一个请求的服务中完成。

    保留重试拦截器并检查您在服务本身或组件上获得的响应以使用后备 URL。

    【讨论】:

    • 对不起丹尼尔,但你在这一点上是错误的。 Angular 4.3+ 拦截器可以处理请求和响应。它基本上只是在 HttpEvent 的 observable 中转换请求的完整周期( request -> resposes ),它被键入为请求或响应事件。看看这个:angular.io/guide/http#intercepting-requests-and-responses
    • 它会拦截响应,但不会停止其他拦截器,因为它们尝试处理相同的 URL。一旦拦截器调用 .next.handle(req) ,它将传递给下一个拦截器,而不管响应如何,因此它不是使用拦截器的正确方法。
    • 但是 OP 只在第二个拦截器中使用 catch,所以如果第一个拦截器已经捕获到特定错误,执行第二个拦截器将不会做任何事情。此外,基于响应的重试发生在第二个拦截器看到响应之前,所以这是非常明智的。
    • 他试图在发现任何错误之前修改标头,但它会在他甚至得到 504 错误之前发生,因此它会添加到每个调用中。
    • @DanielNetzer 对不起,我误解了你的回答
    猜你喜欢
    • 2023-03-13
    • 1970-01-01
    • 2015-04-16
    • 1970-01-01
    • 2015-11-22
    • 2014-03-10
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    相关资源
    最近更新 更多