拦截器
本课属于 Coddy Node.js 学习旅程的“TypeScript 和 NestJS”单元。第 23 课,共 24 课。
你希望每个响应都说明耗时,而不必逐个修改处理程序。拦截器会包装处理程序:next.handle()之前的代码会先运行,而对next.handle()返回结果执行的代码会在之后运行。
intercept(context: ExecutionContext, next: CallHandler) {
console.log('before');
return next.handle().pipe(
tap(() => console.log('after')),
);
}next.handle()会以 RxJS 可观察对象的形式返回处理程序的结果,也就是稍后才会到达的值。.pipe(tap(...))会在值到达时运行一个函数,此时 Nest 还没有发送这个值;这里的pipe与 Nest 的验证管道无关。守卫会在任何拦截器运行之前执行,因此守卫拒绝的请求不会到达拦截器。返回经过管道处理的可观察对象:Nest 会发送它所携带的内容。context.switchToHttp().getResponse()会返回res,它的setHeader方法可以添加响应头。
app.useGlobalInterceptors(...)会将拦截器应用于所有路由;@UseInterceptors(...)则只应用于一个控制器。
挑战
简单运行会使用 src/main.ts 重新启动你的跟踪器。单独的练习文件可以保留在文件树中,这个服务器不会启动它们。
客户端和你自己的监控系统都想知道每个请求花了多长时间。为每个成功的响应添加响应头 X-Response-Time,不要改动任何路由。
- 打开
src/common/response-time.interceptor.ts。在ResponseTimeInterceptor的intercept方法中,先用Date.now()记录当前时间。intercept顶部的代码会在路由的方法运行之前执行。 - 使用
context.switchToHttp().getResponse()获取 Express 的res。 next.handle()会运行路由的方法,并将其结果作为 RxJS 可观察对象返回,也就是稍后才会到达的值。继续返回这个对象,但使用它的.pipe()方法将其传递给tap,它已经从rxjs导入。你传给tap的函数会在结果到达时、Nest 发送结果之前运行。在这个函数中,调用res.setHeader,将X-Response-Time设为从你记录时间到现在经过的毫秒数,后面加上ms,例如3ms。(RxJS 的.pipe()与第 3 章的验证管道无关。它会将一个步骤链接到可观察对象上。)- 在
src/main.ts中,使用app.useGlobalInterceptors(...)为整个应用注册拦截器,传入一个新的ResponseTimeInterceptor,并将它放在管道和过滤器旁边。 - 响应正文不能改变。
GET /tasks仍会返回{ "items": [...], "page": 1, "total": 3, "pages": 2 }。
试一试:发送 GET /tasks,然后打开响应的 Headers 选项卡。你会看到 X-Response-Time,它的值类似 0ms 或 2ms。GET /stats 也会有这个响应头,使用响应头 x-api-key: tasks-key-123 发送的 POST 请求 { "title": "Buy milk" } 也一样。然后不带密钥发送该 POST 请求:返回 403,而且没有 X-Response-Time。Nest 会先运行守卫,再运行拦截器,因此守卫在你的拦截器启动前就拒绝了请求。路由内部抛出的错误也不会带上这个响应头,因为只有结果到达时,tap 才会运行。
亲手试试
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';
@Injectable()
export class ResponseTimeInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
// 记录时间,让处理器运行,然后设置 X-Response-Time。
return next.handle();
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
TypeScript 和 NestJS中的所有课
自己练习: Express 在线编辑器