Reputation: 717
I am calling an API to get random details. The problem is that, sometimes I am getting 502 error as bad gateway and it may also break due to a bad network connection also. Below is my code to API call
// 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);
}
Whenever I get 500 or 502 server error, using an Interceptor I am routing to a error page to notify the user as server issue.
Instead, can I make the API to try one more time in component level or interceptor level if it fails and then route to error page?
// 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");
}));
}
I saw few examples as they are using pipe and adding retryWhen() to achieve this. But as I am very new to angular, I am not able to figure out a way to do it.
Could anyone please help?
Upvotes: 4
Views: 15280
Reputation: 69
Make this function
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..."))
))
)
}
call this inside api url
return this.http.get<RouteData[]>(url).pipe(
this.delayedRetry(10000,4),
catchError(err => {
console.error(err);
return EMPTY;
}),
shareReplay()
);
Upvotes: 1
Reputation: 13515
You can use the retryWhen
operator. The principle behind this is that you throw an error when you don't want to retry.
retryWhen
is effectively a fancy catchError
that will automatically retry unless an error is thrown.
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");
})
);
}
DEMO: https://stackblitz.com/edit/angular-qyxpds
The danger with this is that you will perform continuous requests until the server doesn't return a 500 or 502. In a real-world app you would want to limit retries, and probably put some kind of delay in there to avoid flooding your server with requests.
To do this, you could use take(n)
which will restrict your requests to n
failed attempts. This won't work for you because take
will stop the observable from proceeding to catchError
, and you won't be able to perform navigation.
Instead, you can set a retry limit and throw an error once the retry limit has been reached.
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");
})
);
}
DEMO: https://stackblitz.com/edit/angular-ud1t7c
Upvotes: 5