[angular2]如何建立angular2的http interceptor並且搭配loading bar

集中處理我們的所有http的request和response處理,並且讓讀取的狀態自動顯示和隱藏

前言

早在angularjs的時候,就有http interceptor的概念,可以讓我們很好的封裝和處理這方面的邏輯,而且也可以使用https://github.com/chieffancypants/angular-loading-bar這個元件,自動就會偵測http的request和response來自動完成loading中的效果,但是angular2並沒有http interceptor的範例,所以筆者就只好自己找找資源來實做一下囉,以下則是一些紀錄

導覽

  1. http interceptor的實做
  2. 跟http interceptor整合loading的效果
  3. 結論

 

http interceptor的實做

其實github上面有不少人實做了http interceptor並且open source出來,個人最後選擇了這個版本的https://github.com/voliva/angular2-interceptors,當然首先就是用npm下載下來,然後建立一支http-interceptor.ts

@Injectable()
export class HttpInterceptor implements Interceptor {
  queue = 0;

  constructor() { }

  public interceptBefore(request: InterceptedRequest): InterceptedRequest {
    return request;
  }

  public interceptAfter(response: InterceptedResponse): InterceptedResponse {
    return response;
  }
}

這樣我們就可以實做任何request和response結果的處理,打個比方如果我想要在request之前,在header做些處理,然後在response收到各種錯誤處理碼然後做些處理的話,我們甚至可以定義各種http回傳的state code的enum來讓程式碼更好讀,先建立一支http-state-code.enum.ts

export enum HttpStateCode {
  MultipleChoices = 300,
  Redirect = 302,
  BadRequest = 400,
  Unauthorized = 401,
  Forbidden = 403,
  NotFound = 404,
  NotAcceptable = 406,
  RequestTimeout = 408,
  RequestUriTooLong = 414,
  UnsupportedMediaType = 415,
  RequestedRangeNotSatisfiable = 416,
  InternalServerError = 500,
  ServiceUnavailable = 503
};

接著我們的http interceptor可以改成這樣子

@Injectable()
export class HttpInterceptor implements Interceptor {

  constructor() { }

  public interceptBefore(request: InterceptedRequest): InterceptedRequest {
    request.options.headers.set('Content-Type','application/json');
    return request;
  }

  public interceptAfter(response: InterceptedResponse): InterceptedResponse {
    switch (response.response.status) {
      case HttpStateCode.InternalServerError:
        alert('伺服器故障囉');
        break;
    }
    return response;
  }
}

怎麼實做就自由發揮囉,不過我們還必須要把這個service註冊在module裡面,module是要註冊在根module或建立一個新的module就看個人了,在此示例是建立在app.module.ts裡面

export function interceptorFactory(xhrBackend: XHRBackend, requestOptions: RequestOptions, httpInterceptor: HttpInterceptor) {
  const service = new InterceptorService(xhrBackend, requestOptions);
  service.addInterceptor(httpInterceptor); // Add it here
  return service;
}

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
  ],
  providers: [
    HttpInterceptor,
    {
      provide: InterceptorService,
      useFactory: interceptorFactory,
      deps: [XHRBackend, RequestOptions, HttpInterceptor]
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

因為我們已經實做了這個服務,所以當我們要建立一個http的時候,就要改用我們建立的這個服務,而不是原生的Http囉

constructor(protected http: Http) { }
//改成如下
constructor(protected http: InterceptorService) { }

 

跟http interceptor整合loading的效果

其實ng2的loading bar有很多選擇,也有自動加載的,我們完全不用控制的,可以參考https://github.com/aitboudad/ng-loading-bar,但我個人整合http interceptor的時候發現有點問題,最後我選擇的是自己控制的,可以參考https://github.com/akserg/ng2-slim-loading-bar,接著就來介紹一下筆者個人的整合和做法了,因為在http的時候,我們可能同時會送出好幾個request和收到好幾個response,所以我們沒辦法單純的在request去開啟loading bar然後在response關閉,所以我定義了一個number,在request去++在response去--,這樣子只要數值是0的話,就代表我們可以關閉loadingbar了,示例如下

@Injectable()
export class HttpInterceptor implements Interceptor {
  queue = 0;

  constructor(private loadginBar: SlimLoadingBarService) { }

  public interceptBefore(request: InterceptedRequest): InterceptedRequest {
    this.queue ++;
    this.loadginBar.start();
    request.options.headers.set('Content-Type', 'application/json');
    return request;
  }

  public interceptAfter(response: InterceptedResponse): InterceptedResponse {
    this.queue--;
    this.queue===0 && this.loadginBar.stop();
    switch (response.response.status) {
      case HttpStateCode.InternalServerError:
        alert('伺服器故障囉');
        break;
    }
    return response;
  }

完成之後我們必須要在moduled import這個module

 imports: [
    SlimLoadingBarModule.forRoot() 
  ]

 

 

結論

angular 2的loading效果其實很多做法,有些人是重覆在每次ajax的時候去控制,我個人選擇是統一集中在http的發起和接收的地方做處理,這樣子要抽換效果就更便利囉。