Help us understand the problem. What is going on with this article?

HttpInterceptorの使用例メモ

More than 1 year has passed since last update.

AngularのHttpInterceptorを使った際のメモ

HttpInterceptor

リクエストを飛ばす前やレスポンスを受け取った後に共通の処理を仕込めるものです。

詳細は公式ドキュメントを参照(英語)。
https://angular.io/guide/http#advanced-usage

日本語だと@ponday さんの記事がわかりやすいです。
Angular 4.3で追加されたHttpClientModuleについてのメモ

リクエストの共通設定

ヘッダーを変更したりクッキーを使う場合はリクエストをclone()して上書きします。

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

@Injectable()
export class RequestInterceptor implements HttpInterceptor {
  private baseUrl = 'http://localhost:8080/sample-app';

  constructor() { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // '/api'から始まるリクエスト
    if (request.url.match(/^\/api\//)) {
      const req = request.clone({
        url: `${this.baseUrl}/${request.url}`,  // 接続先URL付加
        withCredentials: true,                  // Cookie有効
        setHeaders: {                           // ヘッダー書き換え
          'Content-Type': 'application/json',
          'X-Requested-With': 'XMLHttpRequest',
        },
      });
      return next.handle(req);
    }
    return next.handle(request);
  }
}

サービスからURL設定への依存を引きはがすとテストが書きやすくなります。

URLの変更についてはHttpInterceptorではなくProxyを使用しても良いです。
https://angular.io/guide/build#proxying-to-a-backend-server

エラーメッセージ表示の自動化

「毎回手動でエラーをcatchしてメッセージ表示」とかやってられないときに。

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

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

  constructor(private toastService: MatSnackBar) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const req = request.clone();
    return next.handle(req).pipe(
      catchError(res => {
        switch (res.status) {
          case 400:
          case 401:
          case 403:
          case 404:
          case 500:
            // {"errors":[{"message":"Sorry, that page does not exist","code":34}]}
            const errors = JSON.parse(res.errors);
            if (errors) {
              errors.map(e => {
                this.toastService.open(`${e.code}: ${e.message}`);
              });
            }
            break;
          default:
            break;
        }
        return throwError(res);
      })
    );
  }
}

トースト表示は自作でも良いです。

プログレスバーの自動表示

プログレスバーの表示/非表示を手動で制御するのは割と面倒なのでインターセプタで自動化しましょう。

import { Injectable } from '@angular/core';
import { HttpEvent, HttpEventType, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap, finalize } from 'rxjs/operators';

// サービスは別途用意
import { ProgressbarService} from './progressbar.service';

@Injectable()
export class ProgressbarInterceptor implements HttpInterceptor {

  constructor(private progressbar: ProgressbarService) { }

  // ↓参考
  // https://github.com/MurhafSousli/ngx-progressbar/blob/master/src/services/interceptor.provider.ts
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.progressbar.show();
    return next.handle(request).pipe(
      //tap(event => {
      //  if (event.type === HttpEventType.UploadProgress) {
      //    const percent = Math.round(100 * event.loaded / event.total);
      //    this.progressbar.set(percent);
      //  }
      //}),
      finalize(() => {
        this.progressbar.hide();
      })
    );
  }
}

( ´ー`) .oO(スピナーも同じようにできるけど画面がちらつきそう) ※ 対策はコメント参照

ISO-8601文字列 → Date型変換

レスポンスが日付を「文字列」で返す場合、そのままだとgetFullYearとか使えなくて辛い。。。
そんなときはインターセプタで文字列抽出してDate型に自動変換しましょう。

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

/**
 * 元ネタは↓のAngularJS用インターセプタ
 * https://gist.github.com/ScottGuymer/9994dae637bb2055d58b
 */
const ISO_8601 = /^\d{4,5}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$/i;
function convertDateStringsToDates(input: any) {
  if (typeof input !== 'object') {
    return input;
  }
  for (const key in input) {
    if (!input.hasOwnProperty(key)) {
      continue;
    }
    const value = input[key];
    let match;
    if (typeof value === 'string' && (match = value.match(ISO_8601))) {
      const milliseconds = Date.parse(match[0]);
      if (!isNaN(milliseconds)) {
        input[key] = new Date(milliseconds);
      }
    } else if (typeof value === 'object') {
      convertDateStringsToDates(value);
    }
  }
}

@Injectable()
export class DateInterceptor implements HttpInterceptor {

  constructor() { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const req = request.clone();
    return next.handle(req).pipe(
      tap(event => {
        if (event instanceof HttpResponse) {
          convertDateStringsToDates(event.body);
        }
      })
    );
  }
}

レスポンスの加工ができるのがHttpInterceptorの良いところですね!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした