AngularにおけるInterceptorの使い方

実務でAngularのInterceptorを使ったらhttpClientの事前・事後処理を共通化できてとても便利だったので軽くInterceptorについてまとめてみました。 たまたま見つけるまで自分は知らなかったのですが、そこそこメジャーな機能なようです。

Interceptorとは?

Angular 4.3から登場した機能で httpclientに割り込み処理を入れることで、リクエストを投げる前やレスポンスを受け取った後に共通の処理を行うことができます。
以下で実務で使えそうなInterceptorでできることを紹介します。

リクエスト時の認証用ヘッダー追加処理を共通化

JWTを使った認証を利用する場合、Authorizationヘッダーにトークンをセットする必要がありますが、通信ごとにヘッダーを追加するのはとてもめんどくさいので interceptorを使ってヘッダーを追加する処理を共通化することができます。 認証用ヘッダー以外のヘッダーも同じ要領で追加することができます。

// src/app/common/auth/token-interceptor.ts

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { CookieService } from 'ngx-cookie-service';
import { Observable } from 'rxjs';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

    constructor(private cookieService: CookieService ) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // Cookieに保存されているJWT(JSON Web Token)を取り出す
        const token = this.cookieService.get('jwt');
        const newRequest = request.clone({
            // 全てのリクエストのAuthorizationヘッダーにトークンをセット
            headers: request.headers.set(
                'Authorization', `Bearer ${token}`
            )
        });
        return next.handle(newRequest)
    }
}

HttpClientに適用するために、上で作成したInterceptorを既存のHTTP_INTERCEPTORSに追加する必要があります。 app,module.tsのprovidersに以下を追加することでHTTP_INTERCEPTORSに追加完了です。

// src/app/app.module.ts

import { TokenInterceptor } from './common/token-interceptor' ;
import { HTTP_INTERCEPTORS } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  providers: [
    // 新たに追加
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

レスポンス時のエラー処理を共通化

ここでは例として1で送ったレスポンスがセッションが切れるなどして、Status Code 401(Unauthorized)が帰ってきたときにログイン画面にルーティングするような処理を共通化します。

// src/app/common/auth/unauthorized-interceptor.ts

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError} from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Router } from '@angular/router';

@Injectable()
export class UnauthorizedInterceptor implements HttpInterceptor {
    constructor(private router: Router) {}
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(
            catchError((error: HttpErrorResponse) => {
                if (error.status === 401) {
                    /**
                     * ログイン画面へのリダイレクトや
                     * 「セッションが切れました」というモーダルの表示などを行う
                     */
                    this.router.navigate(['login']);
                }
                return throwError(error);
            }));
    }
  }

今回も1と同じようにapp,module.tsに { provide: HTTP_INTERCEPTORS, useClass: UnauthorizedInterceptor, multi: true } を追加することで全体で使用可能になります。
また今回は1・2を分けるために別々で書きましたが、一連の処理を同じinterceptorに書くことも可能です。

終わりに

他にもInterceptorを使用することでキャッシュを行ったり、ダミーのレスポンスを返すことができるようですが、そこまで使うことはないと思うので今回は遠慮させていただきます! 興味がある方は調べてみてくださいね。

参考

Top 10 ways to use Interceptors in Angular | by Michael Karén | Angular In Depth | Medium