概念
拦截器
使用@Injectable
装饰器,并实现NestInterceptor
接口。
- 在函数执行之前/之后绑定
额外的逻辑
- 转换从函数返回的结果
- 转换从函数抛出的异常
- 扩展基本函数行为
- 根据所选条件完全重写函数(如缓存目的)
自定义拦截器
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
console.log('before');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`));
);
}
}
- 每个拦截器都有
intercept(ExecutionContext, CallHandler)
方法,第一个参数是执行上下文
,第二个参数是调用处理程序
-
CallHandler
是一个包装执行流的对象。因此。必须手动调用hander()
方法,最终方法才会被触发; - 在函数执行前后添加额外的逻辑。在需要记录与应用程序的交互时很有用。如
存储用户调用
,异步调度事件
,计算时间戳
。
+handle()
返回一个Observable
,可使用tap()
等运算符,在可观察序列的正常/异常终止时调用函数
绑定拦截器
//@UseInterceptors(new LoggingInterceptor())
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
- 拦截器的范围有: 方法层面,控制器层面,全局层面
响应映射
handle()
返回一个Observable
,该流包含了从路由处理程序返回的值,可使用map()运算符
改变它。
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response {
data: T;
}
@Injectable()
export class TransformInterceptor implements NestInterceptor> {
intercept(context: ExecutionContext, next: CallHandler): Observable> {
return next.handle().pipe(map(data => ({ data })));
}
}
- 创建的
TransformInterceptor
将打包响应并将其分配给data属性 - 假如处理程序返回空数组[],请求将被改写成:{ data: [] }
例如需要在全局将每个发生的null值转换成空字符串'',可使用一行代码将拦截器绑定为全局代码。
// 定义拦截器
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class ExcludeNullInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
return next
.handle()
.pipe(map(value => value === null ? '' : value ));
}
}
// 绑定拦截器
const app = await NestFactory.create(ApplicationModule);
app.useGlobalInterceptors(new ExcludeNullInterceptor ());
异常映射
利用catchError()
操作符来覆盖抛出的异常
// exception.interceptor.ts
import {
Injectable,
NestInterceptor,
ExecutionContext,
BadGatewayException,
CallHandler,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
return next
.handle()
.pipe(
catchError(err => throwError(new BadGatewayException())),
);
}
}
Stream重写
假如由于性能问题,需要从缓存中获取。如缓存
拦截器,希望完全阻止调用处理程序,并返回不同的值。
// cache.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, of } from 'rxjs';
@Injectable()
export class CacheInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
const isCached = true;
if (isCached) {
return of([]);
}
return next.handle();
}
}
- 此处有硬编码的isCached变量和响应[],通过
of()
运算符创建并返回一个新的流,因此路由处理程序根本不会被调用; - 为了更好地通用性,可以利用
Reflector
反射器创建自定义修饰符。
更多操作符
Rxjs
运算符操作流提供了很多功能。
// timeout.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from '@nestjs/common';
import { Observable, throwError, TimeoutError } from 'rxjs';
import { catchError, timeout } from 'rxjs/operators';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
return next.handle().pipe(
timeout(5000),
catchError(err => {
if (err instanceof TimeoutError) {
return throwError(new RequestTimeoutException());
}
return throwError(err);
}),
);
};
};
- 5秒后,请求处理将被取消。您还可以在抛出之前添加自定义逻辑RequestTimeoutException(例如,释放资源)。