![](https://img.haomeiwen.com/i5131519/3101dcca3d4ef00e.png)
- 拦截器的功能受面向切面编程
AOP
技术的启发,它可以:- 在函数执行之前/之后绑定额外的逻辑
- 转换从函数返回的结果
- 转换从函数抛出的异常
- 扩展基本函数行为
- 根据所选条件完全重写函数 (例如, 缓存目的)
- 创建拦截器
nest g interceptor xxx
-
logging.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { //拦截器之前 console.log('Before...'); const now = Date.now(); return next.handle() .pipe( tap(() => { //拦截器之后 console.log(`After... ${Date.now() - now}ms`) }), ); } }
- 拦截器必须实现
NestInterceptor<T, R>
接口,并且被@Injectable()
所装饰;-
T
表示已处理的Observable<T>
的类型(在流后面) -
R
表示包含在返回的Observable<R>
中的值的返回类型;
-
-
ExecutionContext
:执行上下文对象,与守卫的对象完全相同; -
CallHandler
:一个包装执行流的对象,必须手动调用它的handler()
方法,否则主程序不会到达控制器,这是因为Nest订阅了返回的流,并使用此流生成的值来为最终用户创建单个/多个响应;
绑定方式
- 与守卫一样,拦截器可以作用域控制器范围内,控制器方法范围内,全局范围内;
- 控制器上
@Controller('cats') @UseInterceptors(LoggingInterceptor) export class CatsController { }
- 全局范围
- 方式一:
main.ts
,模块中不可以依赖注入实例
app.useGlobalInterceptors(new LoggingInterceptor());
- 方式二:在模块上注册,从而可以实现依赖注入实例
import { Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; @Module({ providers: [{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor, }], }) export class ApplicationModule { }
- 方式一:
- 访问与响应过程:
请求到达 --> 拦截器之前 --> 控制器 --> 拦截器之后 --> 响应数据
响应映射
-
handler()
返回一个Observable
,此流中包含控制器返回的响应数据,使用它的map()
运算符可以修改返回数据;import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export interface Response<T> { // 是否存在并不影响响应结果 data: T; } @Injectable() export class TransInterceptor<T> implements NestInterceptor<T, Response<T>> { intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> { return next.handle() .pipe(map(data => ({ data }))); } }
- 运算符
map()
对控制器的返回值做了二次封装,最终响应的数据为{ "data": 控制器的返回值 }
- 还可以对控制器的返回值做判断,对空数据做统一处理
@Injectable() export class ExcludeNullInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next.handle() .pipe(map(value => value === null ? '' : value )); } }
超时处理
-
Observable
的运算符timeout()
:当端点在一段时间内没有任何返回内容时,则抛出异常;import { timeout } from 'rxjs/operators'; @Injectable() export class TimeoutInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next.handle().pipe(timeout(5000)); } }
- 如果请求的控制器(方法)上装饰了相应的异常过滤器,则在超过
5s
还没有获取到控制器的返回值时,进入异常过滤器,由过滤器处理响应。- 捕获一切的异常过滤器
@Catch() export class AllExceptionsFilter implements ExceptionFilter { catch(exception: unknown, host: ArgumentsHost) { let resp = host.switchToHttp().getResponse(); // 发送响应 resp.json({ message: "服务器内部错误!" }); } }
- 控制器
@Controller('cats') @UseFilters(AllExceptionsFilter) export class CatsController { @Get('list') @UseInterceptors(TimeoutInterceptor) async getList() { //延迟6s, 发生了超时 let res = await new Promise(resolve=>{ setTimeout(()=>{ resolve({ Message: 'Success' }) }, 6000); }) return res; } }
- 访问:
http://localhost:3000/cats/list --> {"message": "服务器内部错误!"}
异常映射
Observable
的操作符 catchError
可以覆盖抛出的异常,然后自己处理;
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle()
.pipe(catchError(err => {
// 抛出自己的异常
throwError(new BadGatewayException());
}),);
}
}
重写Stream
- 有时希望完全阻止请求达到处理程序(控制器),并响应不同的值,比如由于性能问题而从缓存中获取;
-
Observable
的运算符of
import { Observable, of } from 'rxjs';
@Injectable()
export class CacheInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// 缓存是否存在
const isCached = true;
if (isCached) {
return of({message: '缓存数据'}); //直接响应缓存数据
}
return next.handle();
}
}
- 为了创建一个通用的解决方案,可以结合反射器
Relector
使用,判别哪些请求做了缓存处理。
请求与响应过程
客户端请求 --> 中间件 --> 守卫 --> 拦截器之前 --> 管道 --> 控制器 --> 拦截器之后 --> 过滤器
- 中间件做请求处理,如```helmet,csrf,rate limiting,compression``等常用的中间件;
- 守卫常用于验证该用户的身份,如果未通过验证,则抛出异常;最适合做权限管理;
- 拦截器之前不能修改请求信息,只能获取请求信息;
- 管道做请求的数据验证和转化,如果验证失败,则抛出异常;
- 控制器负责处理请求和服务桥梁,直接响应服务处理结果;
- 拦截器之后只能修改响应body数据;
- 过滤器:如果前面任何位置发生抛出异常操作,都会直接进入过滤器,否则不经过。
网友评论