HttpInterceptor的使用示例笔记

使用Angular的HttpInterceptor时的注意事项

网络拦截器

在发送请求之前或收到响应之后,可以添加通用处理的东西。

请查阅官方文件以获取详细信息(英文)。
https://angular.io/guide/http#advanced-usage

@ponday さんの記事は、日本語で書かれていて理解しやすいです。メモでは、Angular 4.3で追加されたHttpClientModuleについて述べられています。

共同请求设置

如果要更改标头或使用cookie,可以克隆并覆盖请求。

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的更改,可以使用Proxy而不是HttpInterceptor。
https://angular.io/guide/build#proxying-to-a-backend-server

自动化显示错误消息

当无法每次手动捕捉错误并显示消息时。

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字符串转换为日期类型

如果返回的响应以字符串形式返回日期,直接使用getFullYear之类的函数是无法使用的,这样很麻烦…
在这种情况下,让我们在拦截器中提取字符串并自动将其转换为日期类型。

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的好处在于它可以进行响应的处理!

广告
将在 10 秒后关闭
bannerAds