Menu
CoddyTech

拦截器

本课属于 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(...)则只应用于一个控制器。

challenge icon

挑战

简单

运行会使用 src/main.ts 重新启动你的跟踪器。单独的练习文件可以保留在文件树中,这个服务器不会启动它们。

客户端和你自己的监控系统都想知道每个请求花了多长时间。为每个成功的响应添加响应头 X-Response-Time,不要改动任何路由。

  1. 打开 src/common/response-time.interceptor.ts。在 ResponseTimeInterceptor 的 intercept 方法中,先用 Date.now() 记录当前时间。intercept 顶部的代码会在路由的方法运行之前执行。
  2. 使用 context.switchToHttp().getResponse() 获取 Express 的 res。
  3. next.handle() 会运行路由的方法,并将其结果作为 RxJS 可观察对象返回,也就是稍后才会到达的值。继续返回这个对象,但使用它的 .pipe() 方法将其传递给 tap,它已经从 rxjs 导入。你传给 tap 的函数会在结果到达时、Nest 发送结果之前运行。在这个函数中,调用 res.setHeader,将 X-Response-Time 设为从你记录时间到现在经过的毫秒数,后面加上 ms,例如 3ms。(RxJS 的 .pipe() 与第 3 章的验证管道无关。它会将一个步骤链接到可观察对象上。)
  4. 在 src/main.ts 中,使用 app.useGlobalInterceptors(...) 为整个应用注册拦截器,传入一个新的 ResponseTimeInterceptor,并将它放在管道和过滤器旁边。
  5. 响应正文不能改变。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();
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

TypeScript 和 NestJS中的所有课

自己练习: Express 在线编辑器