Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 5 / HttpInterceptor / Detect (cancelled) xhr

In my angular app I'm seeing chrome (cancel) api calls that fire off too rapidly. I also have an HttpInterceptor that triggers a loading indicator with every HttpClient request after 500ms if the request has not completed. However, on the requests that get (cancelled) there does not seem to be any new event to subsequently hide my loading indicator.

Is there a way to detect 'cancelled' requests in the HttpInterceptor so I can hide my loading indicator again?

  export class GlobalHttpInterceptor implements HttpInterceptor {
    constructor(
      private sessionService: SessionService,
      private loadingService: LoadingService
    ) { }

    intercept(
      req: HttpRequest<any>,
      next: HttpHandler
    ): Observable<HttpEvent<any>> {

      setTimeout(() => {
        if (showLoading) {
          this.loadingService.show();
        }
      }, 500);
      let showLoading = true;

      if (this.sessionService.headers.length > 0) {
        for (const x of this.sessionService.headers) {
          req = req.clone({
            headers: req.headers.set(x.key, x.value)
          });
        }
      }

      return next.handle(req)
        .do(
          (response) => {
            if (response instanceof HttpResponse) {
              showLoading = false;
              this.loadingService.hide();
            }
          },
          (error) => {
            showLoading = false;
            this.loadingService.hide();
          }
        );
    }
  }
like image 628
Justin Lutsky Avatar asked May 04 '18 09:05

Justin Lutsky


2 Answers

Just encountered the same problem. The finalize operator seems to trigger even when the http request is cancelled.

    public intercept(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
        // request starts

        return next.handle(request).pipe(
            finalize(() => {
                // request completes, errors, or is cancelled
            })
        );
    }
like image 108
Davy Avatar answered Sep 21 '22 01:09

Davy


Expanding on Davy's answer, this is how I'm detecting an aborted request.

As he stated, finalize will always run on the observable source completing, which in this case is either a success response, an error response, or an aborted request.

The key to my approach is tracking what you have received so far.

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  let lastResponse: HttpEvent<any>;
  let error: HttpErrorResponse;

  return next.handle(request)
    .pipe(
      tap((response: HttpEvent<any>) => {
        lastResponse = response;
        if (response.type === HttpEventType.Response) {
          console.log('success response', response);
        }
      }),
      catchError((err: any) => {
        error = err;
        console.log('error response', err);
        // TODO: error handling if required
        return throwError(err);        
      }),    
      finalize(() => {
        if (lastResponse.type === HttpEventType.Sent && !error) {
          // last response type was 0, and we haven't received an error
          console.log('aborted request');
        }
      })
    );
}
like image 24
Kurt Hamilton Avatar answered Sep 20 '22 01:09

Kurt Hamilton