Http拦截器就是拦截发出的请求,对其进行统一添加额外处理,然后放行;对响应进行拦截并作出业务上的判断,决定是否给与返回。
常见拦截器
- AuthInterceptor 自定义请求头,如 token 之类
// AuthInterceptor.ts
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,
next: HttpHandler
): Observable> {
const clonedRequest = req.clone({
headers: req.headers.set("X-CustomAuthHeader", "iloveangular")
});
console.log("new headers", clonedRequest.headers.keys());
return next.handle(clonedRequest);
}
}
// AppModule.ts
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 {}
- LogInterceptor 请求日志拦截器,主要记录请求花费时间、状态等
// LogInterceptor.ts
import { 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, 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.ts
import { HttpRequest, HttpResponse } from '@angular/common/http';
export interface Cache {
get(req: HttpRequest): HttpResponse | null;
put(req: HttpRequest, res: HttpResponse): void;
}
export const MAX_CACHE_AGE = 30000; // 单位为毫秒
export interface CacheEntry {
url: string;
response: HttpResponse;
entryTime: number;
}
// CacheService.ts
import { 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();
constructor() {}
get(req: HttpRequest): HttpResponse | 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, res: HttpResponse): 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.ts
import { 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, 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) {
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,
next: HttpHandler
): Observable> {
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}`
)
}
}
}
)
)
}
}