拦截器是一种特殊类型的服务,运行在路由处理程序方法之前和之后。它们在每次请求和响应的生命周期中都被调用,能够在请求处理之前进行操作,或对从处理程序返回的响应进行转换。
NestJS拦截器的用途
- 修改响应数据
统一所有响应数据的格式,为其添加相同的结构或者字段。
- 请求预处理
实现认证和授权逻辑,或对请求数据进行初步校验。
- 性能调优
计算处理请求所需的时间,为优化性能提供依据。
- 日志记录
记录每次请求及响应的详细数据,为问题排查提供信息。
响应数据的统一处理
在这个案例中,我们希望所有的HTTP响应都有一个 code字段和 data字段。我们可以通过创建一个拦截器来执行此操作:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(
map(data => ({ code: 200, data })),
);
}
}
// {
// "code": 200,
// "data": {/* 原来的响应数据 */}
// }
记录处理时间
此拦截器可用于记录处理请求所需的时间,对于性能优化非常有用
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class TimingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
//After... 27ms
Stream overriding 流重载
我们有的时候可能想要完全阻止调用处理程序,而是返回一个不同的值。一个明显的例子是实现一个缓存来提高响应的时间。让我们来看看这个简单的缓存拦截器,它从缓存中返回响应的值。在现实子中,我们可能还需要考虑其他的因素例如TTL、缓存验证、缓存大小等等。
//cache.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } form 'rxjs'
import { Observable, of } from 'rxjs'
@Injectable()
export class CaCheInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const isCached = true;
if (isCachhed) {
return of([])
}
return next.handle()
}
}
我们拦截器有一个isCached变量和一个响应[]。要注意的重点是我们这里返回了一个由RxJS的 of() 操作符创建的新的流。如此路由处理程序就不会被调用。当有人访问这个使用拦截器的节点时,响应就会立刻返回。
使用拦截器
设置拦截器, 我们使用从 @nestjs/common 包导入的 @UseInterceptors() 装饰器。与守卫一样, 拦截器可以是控制器范围内的, 方法范围内的或者全局范围内的。
@UseInterceptors(new LoggingInterceptor())
export class CatsController {}
全局的方式,在模块中使用
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
main.ts 全局引入
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());