1、实现步骤

  • 实现 HttpInterceptor 接口
  1. import { Injectable } from "@angular/core";
  2. import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor } from "@angular/common/http";
  3. import { Observable } from "rxjs";
  4. @Injectable()
  5. export class AuthInterceptor implements HttpInterceptor {
  6. intercept(
  7. req: HttpRequest<any>,
  8. next: HttpHandler
  9. ): Observable<HttpEvent<any>> {
  10. ...
  11. }
  12. }
  • 注册 Provider
  1. import { AuthInterceptor } from "./interceptors/auth.interceptor";
  2. @NgModule({
  3. declarations: [AppComponent],
  4. imports: [BrowserModule, HttpClientModule],
  5. providers: [
  6. { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
  7. ],
  8. bootstrap: [AppComponent]
  9. })
  10. export class AppModule {}

2、常见拦截器

  • AuthInterceptor 自定义请求头,如 token 之类
  1. // AuthInterceptor.ts
  2. import { Injectable } from "@angular/core";
  3. import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor } from "@angular/common/http";
  4. import { Observable } from "rxjs";
  5. @Injectable()
  6. export class AuthInterceptor implements HttpInterceptor {
  7. intercept(
  8. req: HttpRequest<any>,
  9. next: HttpHandler
  10. ): Observable<HttpEvent<any>> {
  11. const clonedRequest = req.clone({
  12. headers: req.headers.set("X-CustomAuthHeader", "iloveangular")
  13. });
  14. console.log("new headers", clonedRequest.headers.keys());
  15. return next.handle(clonedRequest);
  16. }
  17. }
  18. // AppModule.ts
  19. import { AuthInterceptor } from "./interceptors/auth.interceptor";
  20. @NgModule({
  21. declarations: [AppComponent],
  22. imports: [BrowserModule, HttpClientModule],
  23. providers: [
  24. { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
  25. ],
  26. bootstrap: [AppComponent]
  27. })
  28. export class AppModule {}
  • LogInterceptor 请求日志拦截器,主要记录请求花费时间、状态等
  1. // LogInterceptor.ts
  2. import { Injectable } from '@angular/core';
  3. import { HttpInterceptor, HttpRequest, HttpHandler, HttpResponse } from '@angular/common/http';
  4. import { finalize, tap } from 'rxjs/operators';
  5. @Injectable()
  6. export class LogInterceptor implements HttpInterceptor {
  7. constructor(private loggerService: LoggerService) {}
  8. intercept(req: HttpRequest<any>, next: HttpHandler) {
  9. const startTime = Date.now();
  10. let status: string;
  11. return next.handle(req).pipe(
  12. tap(
  13. event => {
  14. status = '';
  15. if (event instanceof HttpResponse) {
  16. status = 'succeeded';
  17. }
  18. },
  19. error => status = 'failed'
  20. ),
  21. finalize(() => {
  22. const elapsedTime = Date.now() - startTime;
  23. const message = req.method + " " + req.urlWithParams +" "+ status
  24. + " in " + elapsedTime + "ms";
  25. console.log(message);
  26. })
  27. );
  28. }
  29. }
  30. // AppModule.ts
  31. @NgModule({
  32. declarations: [AppComponent],
  33. imports: [BrowserModule, HttpClientModule],
  34. providers: [
  35. { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
  36. { provide: HTTP_INTERCEPTORS, useClass: LogInterceptor, multi: true }
  37. ],
  38. bootstrap: [AppComponent]
  39. })
  40. export class AppModule {}
  • CacheInterceptor 请求缓存控制器
  1. // Cache.ts
  2. import { HttpRequest, HttpResponse } from '@angular/common/http';
  3. export interface Cache {
  4. get(req: HttpRequest<any>): HttpResponse<any> | null;
  5. put(req: HttpRequest<any>, res: HttpResponse<any>): void;
  6. }
  7. export const MAX_CACHE_AGE = 30000; // 单位为毫秒
  8. export interface CacheEntry {
  9. url: string;
  10. response: HttpResponse<any>;
  11. entryTime: number;
  12. }
  13. // CacheService.ts
  14. import { Injectable } from "@angular/core";
  15. import { HttpRequest, HttpResponse } from "@angular/common/http";
  16. import { Cache } from "./cache";
  17. import { CacheEntry, MAX_CACHE_AGE } from "./cache.entry";
  18. @Injectable({
  19. providedIn: "root"
  20. })
  21. export class CacheService implements Cache {
  22. cacheMap = new Map<string, CacheEntry>();
  23. constructor() {}
  24. get(req: HttpRequest<any>): HttpResponse<any> | null {
  25. // 判断当前请求是否已被缓存,若未缓存则返回null
  26. const entry = this.cacheMap.get(req.urlWithParams);
  27. if (!entry) return null;
  28. // 若缓存命中,则判断缓存是否过期,若已过期则返回null。否则返回请求对应的响应对象
  29. const isExpired = Date.now() - entry.entryTime > MAX_CACHE_AGE;
  30. console.log(`req.urlWithParams is Expired: ${isExpired} `);
  31. return isExpired ? null : entry.response;
  32. }
  33. put(req: HttpRequest<any>, res: HttpResponse<any>): void {
  34. // 创建CacheEntry对象
  35. const entry: CacheEntry = {
  36. url: req.urlWithParams,
  37. response: res,
  38. entryTime: Date.now()
  39. };
  40. console.log(`Save entry.url response into cache`);
  41. // 以请求url作为键,CacheEntry对象为值,保存到cacheMap中。并执行
  42. // 清理操作,即清理已过期的缓存。
  43. this.cacheMap.set(req.urlWithParams, entry);
  44. this.deleteExpiredCache();
  45. }
  46. private deleteExpiredCache() {
  47. this.cacheMap.forEach(entry => {
  48. if (Date.now() - entry.entryTime > MAX_CACHE_AGE) {
  49. this.cacheMap.delete(entry.url);
  50. }
  51. });
  52. }
  53. }
  54. // CacheInterceptor.ts
  55. import { Injectable } from '@angular/core';
  56. import { HttpInterceptor, HttpRequest, HttpResponse, HttpHandler } from '@angular/common/http';
  57. import { of } from 'rxjs';
  58. import { tap } from 'rxjs/operators';
  59. import { CacheService } from '../cache.service';
  60. const CACHABLE_URL = "http://jsonplaceholder.typicode.com";
  61. @Injectable()
  62. export class CacheInterceptor implements HttpInterceptor {
  63. constructor(private cache: CacheService) {}
  64. intercept(req: HttpRequest<any>, next: HttpHandler) {
  65. // 判断当前请求是否可缓存
  66. if (!this.isRequestCachable(req)) {
  67. return next.handle(req);
  68. }
  69. // 获取请求对应的缓存对象,若存在则直接返回该请求对象对应的缓存对象
  70. const cachedResponse = this.cache.get(req);
  71. if (cachedResponse !== null) {
  72. return of(cachedResponse);
  73. }
  74. // 发送请求至API站点,请求成功后保存至缓存中
  75. return next.handle(req).pipe(
  76. tap(event => {
  77. if (event instanceof HttpResponse) {
  78. this.cache.put(req, event);
  79. }
  80. })
  81. );
  82. }
  83. // 判断当前请求是否可缓存
  84. private isRequestCachable(req: HttpRequest<any>) {
  85. return (req.method === 'GET') && (req.url.indexOf(CACHABLE_URL) > -1);
  86. }
  87. }
  88. // AppModule.ts
  89. @NgModule({
  90. declarations: [AppComponent],
  91. imports: [BrowserModule, HttpClientModule],
  92. providers: [
  93. { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
  94. { provide: HTTP_INTERCEPTORS, useClass: LogInterceptor, multi: true },
  95. { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true }
  96. ],
  97. bootstrap: [AppComponent]
  98. })
  99. export class AppModule {}
  • ResponseInterceptor 接口错误统一处理
  1. import { Injectable } from '@angular/core'
  2. import {
  3. HttpEvent,
  4. HttpRequest,
  5. HttpResponse,
  6. HttpErrorResponse,
  7. HttpHandler,
  8. HttpInterceptor
  9. } from '@angular/common/http'
  10. import { Observable } from 'rxjs'
  11. import { Router } from '@angular/router'
  12. import { tap } from 'rxjs/operators'
  13. import { MessageService } from '../providers'
  14. @Injectable()
  15. export class ResponseInterceptor implements HttpInterceptor {
  16. constructor(private messageService: MessageService, private router: Router) {}
  17. intercept(
  18. req: HttpRequest<any>,
  19. next: HttpHandler
  20. ): Observable<HttpEvent<any>> {
  21. return next.handle(req).pipe(
  22. tap(
  23. response => {
  24. if (response instanceof HttpResponse) {
  25. if (
  26. response.status === 200 &&
  27. response.body &&
  28. parseInt(response.body.code, 10) !== 0
  29. ) {
  30. const message = response.body.message || '未知错误'
  31. if (response.body.code === 109) {
  32. // token 失效
  33. this.router.navigate(['/login'])
  34. }
  35. this.messageService.addErrorMessage(message)
  36. }
  37. }
  38. },
  39. error => {
  40. if (error instanceof HttpErrorResponse) {
  41. if (error.status >= 500) {
  42. this.messageService.addErrorMessage(
  43. `服务器内部错误 ${error.status}`
  44. )
  45. } else if (error.status >= 400 && error.status < 500) {
  46. this.messageService.addErrorMessage(
  47. `客户端参数错误 ${error.status}`
  48. )
  49. }
  50. }
  51. }
  52. )
  53. )
  54. }
  55. }