Angular 5拦截器 - 仅在第一个拦截器重试失败后调用第二个拦截器

4
我正在构建一个Angular 5应用程序,其中我有两个拦截器:
  • 一个用于重试失败的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 })
        }
      })
  });
 }
}
3个回答

3

您的问题在于提供拦截器的顺序。您首先提供了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 {
}

请再读一遍我的回答。它关于提供的顺序。 - Ingo Bürk
请检查 OP StackBlitz,它们已经按照正确顺序排列。这不是问题的原因。 - Daniel Netzer
我不知道我们是否在看不同的链接,但是我在发布的 Stackblitz 上看到的是,在 app.module.ts 中 I1 是第一位,I2 是第二位。 - Ingo Bürk
更改顺序似乎已经解决了问题。我不知道有这种处理响应的方式。但是在重试后,我该如何将HttpError传递给I2?这样只传递消息,但我也想在I2上获得HttpErrorResponse。谢谢。 - HobojoeBr
@HobojoeBr,请看我的答案 - Max Koretskyi

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)
        )
    })
);

你可以在这里了解更多关于创建可观察对象的内容。


谢谢!你的解决方案有效,但我无法添加延迟,可能是因为我对RXJS还不熟悉。但是非常感谢你的帮助! - HobojoeBr
@HobojoeBr,只需添加delay运算符。我已更新我的代码。你在mergeMap中使用index的解决方案可能比我的更容易理解 :) - Max Koretskyi
太好了,我在 RxJs 方面还有很长的路要走!谢谢。 - HobojoeBr
@AngularInDepth.com:您介意看一下我在以下问题中遇到的几乎相同的问题吗?(https://stackoverflow.com/questions/50024524/how-to-handle-error-when-implementing-exponential-backoff-function?noredirect=1#comment87066773_50024524)。我想实现您的解决方案,但只是无法弄清楚如何在我的代码中实现它。谢谢。 - k.vincent
@k.vincent,我有时间时会看一下。 - Max Koretskyi

-2

我不确定这是处理这种情况的正确方法,拦截器用于检查请求,而不是响应,它们不关心返回的响应,因为你在看到响应之前就通过.next传递了请求。

似乎更适合在服务中完成,该服务检查响应,然后对其他资源进行另一个请求。

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


1
抱歉,但是你在这一点上是错的丹尼尔。Angular 4.3+ 拦截器可以处理请求和响应。它基本上将请求的整个周期(请求->响应)转换为一个可观察的 HttpEvent,它们被类型化为请求或响应事件。请看这个链接:https://angular.io/guide/http#intercepting-requests-and-responses - Pierre Mallet
它将拦截响应,但不会停止其他拦截器的工作,因为它们尝试处理相同的URL。一旦拦截器调用.next.handle(req),它将传递给下一个拦截器,无论响应如何,因此这不是使用拦截器的正确方式。 - Daniel Netzer
他正在尝试在捕获任何错误之前修改标头,但这将在他甚至收到504错误之前发生,因此它将添加到每个调用中。 - Daniel Netzer
如果问题中没有提到被注释掉的代码,我会忽略它。这个问题是关于重试特定错误并最终显示失败请求的错误,所以我不认为那里有修改头文件的联系。但只有 OP 确定了,我猜。 - Ingo Bürk
@DanielNetzer,是我发的问题,但由于您已经回答了并且没有被编辑过,所以我无法为它点赞。 - Pierre Mallet
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接