在项目中,前端需要在每次发送给后端的请求的header中统一添加 token 信息或者其他统一操作,这个时候就用到了拦截器。
在 Angular 中,创建一个拦截器使用到了 @angular/common/http
中的HttpInterceptor
,实现的具体步骤如下:
创建一个拦截器服务
创建一个服务
因为这按类别分,也是一个服务,所以使用Angular/CLI创建时,执行命令:
ng g s demoInterceptor
执行如下:
➜ service git:(master) ✗ ng g s demoInterceptor
CREATE src/app/public/service/demo-interceptor.service.spec.ts (379 bytes)
CREATE src/app/public/service/demo-interceptor.service.ts (144 bytes)
修改服务实现接口 HttpInterceptor
修改 demo-interceptor.service.ts 如下:
export class DemoInterceptorService implements HttpInterceptor {
constructor() {
}
}
修改服务实现接口 HttpInterceptor 中的intercept方法
查看接口:HttpInterceptor 的源码,会发现这里需要实现 intercept 方法;
所以修改 demo-interceptor.service.ts 如下:
import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DemoInterceptorService implements HttpInterceptor {
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req);
}
}
此时一个不进行任何拦截操作的拦截器就创建成功了!
这里会通过next对象,将req 传给下一个拦截器,如果没有下一个拦截器,就会把请求传给HttpClient的后端处理器,从而发送请求,获取数据
给应用注入拦截器
在有HttpClient模块的上级模块,或者同一个模块中注入拦截器,如果在根模块中导入了HttpClientModule
,那么就要在根模块中注入拦截器。
修改根模块(app.module.ts)的providers数组
providers: [
{
provide: NZ_I18N,
useValue: zh_CN
},
{
provide: LocationStrategy,
useClass: HashLocationStrategy
},
{
provide: HTTP_INTERCEPTORS,
useClass: DemoInterceptorService,
multi: true
},
],
需要注意的是
multi: true
这个选项是必须要有的,这个选项是要让Angular 知道HTTP_INTERCEPTORS注入的是一个多个值的数组,而不是单一的一个值。
添加拦截器中的逻辑
由于 HttpRequest 的属性是readonly 的,所以不可以直接修改 HttpRequest,如果需要修改,可以使用clone()
方法
在header中添加 Basic 认证信息
修改 demo-interceptor.service.ts 如下:
import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DemoInterceptorService implements HttpInterceptor {
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const basicParam = btoa('demoBasic');
const request = req.clone({headers: req.headers.append('Authorization', 'Basic ' + basicParam)});
return next.handle(request);
}
}
这里的basicParam 是通过
btoa()
方法将'demoBasic'
字符串转换成base64,request是将原有的 HttpRequest 增加了Basic 认证信息以后克隆新的request,在通过next.handle将这个新的 HttpRequest 传给下一个拦截器(如果有的话),或者传给 HttpClient 的后端处理器。
清空请求体
当需要判断某个请求需要清空请求体的时候,需要将克隆后的请求体的body 设置为null
而不是undefined
,设置为undefined
,请求体是保持不变的。
具体如下:
import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DemoInterceptorService implements HttpInterceptor {
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// 增加Basic认证
const basicParam = btoa('demoBasic');
const request = req.clone({headers: req.headers.append('Authorization', 'Basic ' + basicParam)});
// 清空请求体
const requestNoBody = req.clone({body: null});
return next.handle(requestNoBody);
}
}
捕获错误
有时需要处理请求发生错误;
具体如下:
import {Injectable} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
import {Observable, of, throwError} from 'rxjs';
import {catchError} from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class DemoInterceptorService implements HttpInterceptor {
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// 增加Basic认证
const basicParam = btoa('demoBasic');
const request = req.clone({headers: req.headers.append('Authorization', 'Basic ' + basicParam)});
// 清空请求体
const requestNoBody = req.clone({body: null});
return next.handle(request).pipe(catchError((error: HttpErrorResponse) => this.handleErrorResponse(error)));
}
private handleErrorResponse(event: HttpResponse<any> | HttpErrorResponse): Observable<any> {
// 处理 event
// ...
// ...
return throwError(event);
}
}