1、实现步骤
import { Injectable } from "@angular/core";import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor } from "@angular/common/http";import { Observable } from "rxjs";@Injectable()export class AuthInterceptor implements HttpInterceptor { intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { ... }}
import { AuthInterceptor } from "./interceptors/auth.interceptor";@NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ], bootstrap: [AppComponent]})export class AppModule {}
2、常见拦截器
- AuthInterceptor 自定义请求头,如 token 之类
// AuthInterceptor.tsimport { Injectable } from "@angular/core";import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor } from "@angular/common/http";import { Observable } from "rxjs";@Injectable()export class AuthInterceptor implements HttpInterceptor { intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { const clonedRequest = req.clone({ headers: req.headers.set("X-CustomAuthHeader", "iloveangular") }); console.log("new headers", clonedRequest.headers.keys()); return next.handle(clonedRequest); }}// AppModule.tsimport { AuthInterceptor } from "./interceptors/auth.interceptor";@NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ], bootstrap: [AppComponent]})export class AppModule {}
- LogInterceptor 请求日志拦截器,主要记录请求花费时间、状态等
// LogInterceptor.tsimport { Injectable } from '@angular/core';import { HttpInterceptor, HttpRequest, HttpHandler, HttpResponse } from '@angular/common/http';import { finalize, tap } from 'rxjs/operators';@Injectable()export class LogInterceptor implements HttpInterceptor { constructor(private loggerService: LoggerService) {} intercept(req: HttpRequest<any>, next: HttpHandler) { const startTime = Date.now(); let status: string; return next.handle(req).pipe( tap( event => { status = ''; if (event instanceof HttpResponse) { status = 'succeeded'; } }, error => status = 'failed' ), finalize(() => { const elapsedTime = Date.now() - startTime; const message = req.method + " " + req.urlWithParams +" "+ status + " in " + elapsedTime + "ms"; console.log(message); }) ); }}// AppModule.ts@NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: LogInterceptor, multi: true } ], bootstrap: [AppComponent]})export class AppModule {}
// Cache.tsimport { HttpRequest, HttpResponse } from '@angular/common/http';export interface Cache { get(req: HttpRequest<any>): HttpResponse<any> | null; put(req: HttpRequest<any>, res: HttpResponse<any>): void;}export const MAX_CACHE_AGE = 30000; // 单位为毫秒export interface CacheEntry { url: string; response: HttpResponse<any>; entryTime: number;}// CacheService.tsimport { Injectable } from "@angular/core";import { HttpRequest, HttpResponse } from "@angular/common/http";import { Cache } from "./cache";import { CacheEntry, MAX_CACHE_AGE } from "./cache.entry";@Injectable({ providedIn: "root"})export class CacheService implements Cache { cacheMap = new Map<string, CacheEntry>(); constructor() {} get(req: HttpRequest<any>): HttpResponse<any> | null { // 判断当前请求是否已被缓存,若未缓存则返回null const entry = this.cacheMap.get(req.urlWithParams); if (!entry) return null; // 若缓存命中,则判断缓存是否过期,若已过期则返回null。否则返回请求对应的响应对象 const isExpired = Date.now() - entry.entryTime > MAX_CACHE_AGE; console.log(`req.urlWithParams is Expired: ${isExpired} `); return isExpired ? null : entry.response; } put(req: HttpRequest<any>, res: HttpResponse<any>): void { // 创建CacheEntry对象 const entry: CacheEntry = { url: req.urlWithParams, response: res, entryTime: Date.now() }; console.log(`Save entry.url response into cache`); // 以请求url作为键,CacheEntry对象为值,保存到cacheMap中。并执行 // 清理操作,即清理已过期的缓存。 this.cacheMap.set(req.urlWithParams, entry); this.deleteExpiredCache(); } private deleteExpiredCache() { this.cacheMap.forEach(entry => { if (Date.now() - entry.entryTime > MAX_CACHE_AGE) { this.cacheMap.delete(entry.url); } }); }}// CacheInterceptor.tsimport { Injectable } from '@angular/core';import { HttpInterceptor, HttpRequest, HttpResponse, HttpHandler } from '@angular/common/http';import { of } from 'rxjs';import { tap } from 'rxjs/operators';import { CacheService } from '../cache.service';const CACHABLE_URL = "http://jsonplaceholder.typicode.com";@Injectable()export class CacheInterceptor implements HttpInterceptor { constructor(private cache: CacheService) {} intercept(req: HttpRequest<any>, next: HttpHandler) { // 判断当前请求是否可缓存 if (!this.isRequestCachable(req)) { return next.handle(req); } // 获取请求对应的缓存对象,若存在则直接返回该请求对象对应的缓存对象 const cachedResponse = this.cache.get(req); if (cachedResponse !== null) { return of(cachedResponse); } // 发送请求至API站点,请求成功后保存至缓存中 return next.handle(req).pipe( tap(event => { if (event instanceof HttpResponse) { this.cache.put(req, event); } }) ); } // 判断当前请求是否可缓存 private isRequestCachable(req: HttpRequest<any>) { return (req.method === 'GET') && (req.url.indexOf(CACHABLE_URL) > -1); }}// AppModule.ts @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: LogInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ], bootstrap: [AppComponent]})export class AppModule {}
- ResponseInterceptor 接口错误统一处理
import { Injectable } from '@angular/core'import { HttpEvent, HttpRequest, HttpResponse, HttpErrorResponse, HttpHandler, HttpInterceptor} from '@angular/common/http'import { Observable } from 'rxjs'import { Router } from '@angular/router'import { tap } from 'rxjs/operators'import { MessageService } from '../providers'@Injectable()export class ResponseInterceptor implements HttpInterceptor { constructor(private messageService: MessageService, private router: Router) {} intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { return next.handle(req).pipe( tap( response => { if (response instanceof HttpResponse) { if ( response.status === 200 && response.body && parseInt(response.body.code, 10) !== 0 ) { const message = response.body.message || '未知错误' if (response.body.code === 109) { // token 失效 this.router.navigate(['/login']) } this.messageService.addErrorMessage(message) } } }, error => { if (error instanceof HttpErrorResponse) { if (error.status >= 500) { this.messageService.addErrorMessage( `服务器内部错误 ${error.status}` ) } else if (error.status >= 400 && error.status < 500) { this.messageService.addErrorMessage( `客户端参数错误 ${error.status}` ) } } } ) ) }}