Intercepting HTTP Calls

In addition to return types and JSON parsing, HttpClient introduces interceptors—a tool that exposes any AJAX request made in the application for modification. Let’s add two of these. One, a conditional retry service, attempts to intelligently rerun failed HTTP requests behind the scenes before the user sees an error state and manually retries. The other listens in to every request and on an unreconcilable error, cleanly displays an error message (as opposed to a flow-breaking alert or a silent failure).

The first step to building an interceptor is to generate a service to hold that structure. Generate a new service with ng g service retry-interceptor and open it. Modify it to add the following:

 import​ { Injectable } ​from​ ​'@angular/core'​;
 import​ { Observable } ​from​ ​'rxjs'​;
 import​ {
  HttpResponse,
  HttpErrorResponse,
  HttpEvent,
  HttpInterceptor,
  HttpRequest,
  HttpHandler
} ​from​ ​'@angular/common/http'​;
 
 @Injectable({
  providedIn: ​'root'
 })
export​ ​class​ RetryInterceptorService ​implements​ HttpInterceptor {
 
 constructor​() { }
 
intercept(request: HttpRequest<any>, next: HttpHandler):
  Observable<HttpEvent<any>> {
  }
 }

There are a lot of imports from HTTP here, but don’t worry about understanding all of them at this point.

The HttpInterceptor interface is what tells Angular about the role of this service—you’ll see it come into play later in this section.

The meat of an interceptor service is in the well-named intercept method. The first parameter, request, is an immutable object containing details about the request itself. To modify the actual request, you’d use the clone method. The next parameter is a tool that lets us run the rest of the interceptors and finally sends the full request.

In this case, the interceptor is concerned with what happens after the request is sent off, so the body of intercept needs to pass off the request to next.handle before it does anything. Fill in the body of intercept with the following:

 return​ next.handle(request)
 .pipe(
  retryWhen(err$ =>
  err$
  .pipe(
  flatMap(err => {
 if​ (err ​instanceof​ HttpErrorResponse
  && err.status < 600 && err.status > 499) {
return​ ​of​(​null​)
.pipe(delay(500));
  }
return​ throwError(err);
  })
  )
  )
 );

If the request failed with a 5xx error, we return an observable of nothing to indicate that the request should be retried.

Before it retries, the inner observable adds a delay of 500 ms to ensure the server doesn’t get overloaded with multiple simultaneous retries.

In the case of any other error, the inner observable rethrows the error so that later on, error handlers are aware of what’s happening.

The RetryInterceptor could just use the retry operator from Chapter 3, Managing Asynchronous Events, but that would mean retrying every non-2xx request, including the 4xx class of errors where no amount of retrying will fix the problem, or worse, retrying when there’s an unrecoverable syntax error. Instead, we use retryWhen, which allows us to handle the error as an observable stream, optionally retrying after a check to ensure the status code is in the 500 class. The retryWhen operator merrily passes along values unmodified until the parent observable emits an error event. In that case, retryWhen emits a regular next event to the inner observable, containing the error. If the inner observable throws an error, then no retry happens.

Now that the interceptor is built out, you need to register it with its parent module. HttpInterceptors are special cases. Open up app.module.ts and update your providers array with the new object (PhotosService should already be there). The special HTTP_INTERCEPTORS provider informs Angular that this isn’t just any old service, but rather one that has a specific purpose listening in to HTTP calls.

 providers: [
  {
  provide: HTTP_INTERCEPTORS,
  useClass: RetryInterceptorService,
  multi: ​true
  }
 ],

This interceptor can attempt to retry a few times, but at some point, it’s time to admit defeat and inform the user that the request has failed. Run ng g service failure-interceptor and fill it out the same way you did with the RetryInterceptor. We use the tap operator here to tap into the returned request. Like subscribe, tap also optionally takes a Subscriber, so we can use the same trick as the save photo method. Many things can go wrong, so our method checks to ensure that the error actually has to do with AJAX before it displays the error.

 return​ next.handle(request).​do​({
  error: (err: any) => {
 if​ (err ​instanceof​ HttpErrorResponse) {
 let​ msg = ​`​${err.status}​: ​${err.message}​`​;
 this​.errorService.showError(msg);
  }
 });
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.70.21