Angular2でHTTP Interceptorを使いたい
2016-04-12

Angular2でHTTP Interceptorを使いたい

Way of httpInterceptor in Angular 2

4/10に開催されたAngular 2ハンズオンのチューターとして参加してきました。
チューターしている合間に、Angular2でのHttpInterceporのやり方を調べてたので、その辺りを紹介します。

ついでにLTしてきました。LTの資料はこちらです。

Angular2 HttpInterceptor Needed
https://gist.github.com/mitsuruog/6f7f0ca3f546b245bccdd3ebc14375d8

HttpInterceptorとは

HttpInterceptorとは、XHR(つまりAjax)に関連する横断的機能(cross-cutting feature)を分離するためのものです。

Controllerなどの後続処理から、XHR通信時に発生する例外処理などを分離してHttpInterceptorで一括処理します。
Angular1では、HttpInterceptorを使うことで綺麗に分離することができました。

refs: AngularJS: API: $http

Angular2では簡単にはできない

Angular2でも簡単に実現できると考えていたのですが、思ったより面倒でした。
公式リポジトリのIssueにて実現方法について議論されていました。(めっちゃ長いです)

RFC: Http interceptors and transformers · Issue #2684 · angular/angular

Angualr2でのHttpInterceptorの実現方法

以下の手順で実現できます。

  1. Httpを継承したCustomHttpを作成する。
  2. bootstrapにて、既存のHttpプロバイダをOvarrideする。

では順に見て行きましょう。

CustomHttpを作成する

まず、CustomHttpを作成します。HttpのAPIを1つ1つWarpします。
例外が発生した場合は、catchで捕捉してprivate関数のhandleResponseErrorで一括処理させるようにしています。

custom-http.service.ts

import {Http, ConnectionBackend, RequestOptions, Request, RequestOptionsArgs, Response} from "angular2/http";
import {Observable} from "rxjs/Observable";

export class CustomHttp extends Http {

  constructor(backend:ConnectionBackend,
              defaultOptions:RequestOptions) {
    super(backend, defaultOptions)
  }

  request(url:string | Request, options?:RequestOptionsArgs):Observable<Response> {
    return super.request(url, options).catch((res: Response) => this.handleResponseError(res));
  }

  get(url:string, options?:RequestOptionsArgs):Observable<Response> {
    return super.get(url, options).catch((res: Response) => this.handleResponseError(res));
  }

  // ...長いので省略

  //
  // Here's to handle error stuff
  //
  private handleResponseError(res:Response) {
    if (res.status === 401) {
      // do something
    } else if (res.status === 500) {
      // do something
    }
    return Observable.throw(res);
  }
}

Httpを上書きする。

つづいて、bootstrap時にHttpCustomHttpで上書きします。
次のコードは、provideを利用して、Angular2がDIを処理する際にインスタンスを生成する方法を変更しています。

main.ts

import 'rxjs/Rx';  // RxJS本体のObservableを利用するための設定

import {bootstrap} from 'angular2/platform/browser';
import {provide} from "angular2/core";

import {AppComponent} from './app.component';
import {CustomHttp} from "./common/services/custom-http.service";

bootstrap(AppComponent, [
  provide(Http, {
    useFactory: (backend:XHRBackend, defaultOptions:RequestOptions) => {
      return new CustomHttp(backend, defaultOptions)
    },
    deps: [XHRBackend, RequestOptions]
  })
]);

まとめ

Angular2でもHttpInterceptorを実現できそうですが、少し面倒でした。

こちらにcustom-http.service.tsのコード置いておきます。(しばらくはこのままで使えるはず。。。)

custom-http.service.ts

(追記)
LTではnew Providor()としていましたが、 @laco0416さんから、
ProvidorをWarpしているprovideを利用した方が破壊的変更が起きにくいとアドバイスいただき変更しました。ありがとうございます!!