Angular 5 拦截器 - 仅在第一个拦截器重试失败后调用第二个拦截器
Angular 5 Interceptor - Only call second interceptor after failed retry on first interceptor
我正在构建一个 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 })
}
})
});
}
}
我不确定这是处理此类事情的正确方法,拦截器用于检查请求,而不是响应,它们不关心返回的响应,因为您在看到响应。
似乎更适合在检查响应然后对其他资源发出另一个请求的服务中完成。
保留重试拦截器并检查您在服务本身或组件上获得的响应以使用回退 URL。
您的问题是您提供两个拦截器的顺序。您首先提供 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 {
}
这是一种实现方式 - 将其应用到您的代码中:
// 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)
)
})
);
您可以了解有关创建可观察对象的更多信息here。
我正在构建一个 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 })
}
})
});
}
}
我不确定这是处理此类事情的正确方法,拦截器用于检查请求,而不是响应,它们不关心返回的响应,因为您在看到响应。
似乎更适合在检查响应然后对其他资源发出另一个请求的服务中完成。
保留重试拦截器并检查您在服务本身或组件上获得的响应以使用回退 URL。
您的问题是您提供两个拦截器的顺序。您首先提供 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 {
}
这是一种实现方式 - 将其应用到您的代码中:
// 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)
)
})
);
您可以了解有关创建可观察对象的更多信息here。