web-dev-qa-db-ja.com

Angular 5 / HttpInterceptor / Detect(cancelled)xhr

私のangularアプリでは、chrome(cancel)api呼び出しが急速に発生します。また、すべての負荷インジケーターをトリガーするHttpInterceptorがあります。リクエストが完了していない場合、500ms後のHttpClientリクエスト。ただし、取得(キャンセル)されたリクエストでは、後で読み込みインジケーターを非表示にする新しいイベントがないようです。

HttpInterceptorで「キャンセルされた」リクエストを検出して、読み込みインジケーターを再び非表示にする方法はありますか?

  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();
          }
        );
    }
  }
11
Justin Lutsky

同じ問題が発生しました。 finalize演算子は、httpリクエストがキャンセルされた場合でもトリガーされるようです。

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
        })
    );
}
15
Davy

Davyの答えを拡張すると、これが中止されたリクエストを検出する方法です。

彼が述べたように、finalizeは常に監視可能なソースの完了時に実行されます。この場合、これは成功応答、エラー応答、または中止された要求のいずれかです。

私のアプローチの鍵は、これまでに受け取ったものを追跡することです。

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');
        }
      })
    );
}
0
Kurt Hamilton

@ Davyによって提供された回答がまだ緑色のチェックマークでマークされていない理由がわかりません。これは、中止可能なフェッチに固執することなく使用できる実用的なソリューションです。

これが機能しています Angular 8 で構築されたデモ。

ネットワークスロットリング(「Slow3G」など)を試してみて、アプリケーションのリクエストキューに少なくとも1つのHTTPリクエストがあるたびに表示される派手なアニメーションの円に注目してください(詳細については、ソースコードを参照してください)。 )。

ネットワークリクエストをよく見ると、ユーザーから新しい入力を受信すると、以前の保留中のXHRリクエストがキャンセルされていることがわかります(GitHubAPIはHTTPGETリクエストの数に制限を課しているため、賢明に使用してください。 403 HTTPステータスコードが返されます)。 @Davyによって提案されたアプローチは、保留中の要求があるときに何か凝ったことをしてCmd + S(PCではCtrl + S)を押さない限り、正常に機能します。その場合、プロセス全体がネイティブのモーダルダイアログによって中断されたため、ファイナライズハンドラーは呼び出されません。