1. 程式人生 > 其它 >Angular 如何通過 HTTP Interceptor 實現 HTTP 請求的超時監控

Angular 如何通過 HTTP Interceptor 實現 HTTP 請求的超時監控

當開發人員在 Dynatrace 中檢視這些請求時,將無法再看到超時後正在進行的 API 呼叫。 該過程在後臺進行渲染,但 Dynatrace 看到返回給客戶端的響應並停止記錄,在這種情況下,如果能瞭解它正在做的事情需要這麼長時間,對分析問題會更有幫助。

我們可以引入一個 Angular HTTP_INTERCEPTOR 來超時等待已久的網路請求,從而確保在伺服器端引導的應用程式具有更短的生命週期。 換句話說:所以 SSR 渲染不會因為等待來自網路的緩慢 API 響應而 掛起。 但是,這可能需要在應用程式程式碼甚至 SSR 程式碼中新增額外的邏輯,以便在 SSR 響應中不會返回此類格式錯誤的渲染(基於不完整的資料)。 在這種情況下,最好回退到沒有快取標頭的 CSR 應用程式,而不是允許格式錯誤的渲染 html 作為響應傳送(並且可能由 CDN 快取)。

一個例子。

在 app.module.ts 裡的程式碼:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RequestTimeoutHttpInterceptor, DEFAULT_TIMEOUT } from './interceptors';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
  ],
  declarations: [
    AppComponent,
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: RequestTimeoutHttpInterceptor, multi: true },
    { provide: DEFAULT_TIMEOUT, useValue: 5000 },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

interceptor 的實現:

import { Injectable, InjectionToken, Inject } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { empty, TimeoutError } from 'rxjs';
import { timeout, catchError } from 'rxjs/operators';

export const DEFAULT_TIMEOUT = new InjectionToken<number>('defaultTimeout');

@Injectable({
  providedIn: 'root'
})
export class RequestTimeoutHttpInterceptor implements HttpInterceptor {

  constructor(
    @Inject(DEFAULT_TIMEOUT) protected defaultTimeout: number,
  ) { }

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const modified = req.clone({
      setHeaders: { 'X-Request-Timeout': `${this.defaultTimeout}` }
    });

    return next.handle(modified).pipe(
      timeout(this.defaultTimeout),
      catchError(err => {
        if (err instanceof TimeoutError)
          console.error('Timeout has occurred', req.url);
        return empty();
      })
    );
  }

}

這裡使用了 rxjs 的 timeout 操作符。如果在指定的時間間隔之內沒有 emit 值,則會丟擲 error.

看下面這個例子:

// RxJS v6+
import { of } from 'rxjs';
import { concatMap, timeout, catchError, delay } from 'rxjs/operators';

// simulate request
function makeRequest(timeToDelay) {
  return of('Request Complete!').pipe(delay(timeToDelay));
}

of(4000, 3000, 2000)
  .pipe(
    concatMap(duration =>
      makeRequest(duration).pipe(
        timeout(2500),
        catchError(error => of(`Request timed out after: ${duration}`))
      )
    )
  )
  /*
   *  "Request timed out after: 4000"
   *  "Request timed out after: 3000"
   *  "Request Complete!"
   */
  .subscribe(val => console.log(val));

在這段程式碼裡,我們首先使用 delay 操作符,在 makeRequest 函式裡指定了一個時間間隔,來模擬函式呼叫的延遲。

然後將 makeRequest 返回的 Observable,添加了一個 timeout(2500) 的操作符,意思是 2.5 秒之內,如果該 Observable 沒有發出值,即進入 CatchError 的處理邏輯內。

資料來源頭有三個值,4000,3000 和 2000,其中只有最後一個值小於 2500,因此能在超時時間間隔之內完成函式呼叫。其他兩個值都會導致超時,從而進入 catchError 的資料列印。