在 Angular 中订阅后 API 调用失败时如何重试 2+
How to retry when the API calls fails after subscribing in Angular 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 在组件级别或拦截器级别再试一次,然后路由到错误页面吗?
// 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 的新手,我无法找到一种方法。
有人可以帮忙吗?
您可以使用 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
限制重试
这样做的危险在于,您将执行连续的请求,直到服务器没有 return 500 或 502。在真实世界的应用程序中,您可能希望限制重试,并且可能会放置一些类型在那里延迟以避免请求淹没您的服务器。
为此,您可以使用 take(n)
,这会将您的请求限制为 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");
})
);
}
创建这个函数
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()
);
我打电话给 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 在组件级别或拦截器级别再试一次,然后路由到错误页面吗?
// 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 的新手,我无法找到一种方法。
有人可以帮忙吗?
您可以使用 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
限制重试
这样做的危险在于,您将执行连续的请求,直到服务器没有 return 500 或 502。在真实世界的应用程序中,您可能希望限制重试,并且可能会放置一些类型在那里延迟以避免请求淹没您的服务器。
为此,您可以使用 take(n)
,这会将您的请求限制为 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");
})
);
}
创建这个函数
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()
);