中间件 (Middleware)
中间件在路由之前执行,最接近 Express 层。见官方请求生命周期。
中间件是在路由处理器之前执行的函数,可以访问请求和响应对象,并能修改它们或结束请求-响应周期。NestJS 的中间件与 Express/Fastify 的中间件概念一致,但额外支持依赖注入。
一、中间件的特点
| 特性 | 说明 |
|---|---|
| 可注入依赖 | 与 Nest 的 IoC 容器集成,可以注入服务(如 LoggerService) |
| 执行顺序 | 位于守卫和管道之前,即先经过中间件,再进入守卫,最后到管道和控制器 |
| 与普通中间件一致 | 可以调用 next() 传递给下一个中间件,或者直接发送响应终止流程 |
| 局限性 | 无法访问 ExecutionContext,不能像守卫那样获取执行的控制类/方法元数据 |
执行顺序示意:
请求 → 中间件 → 守卫 → 管道(验证/转换)→ 控制器 → 拦截器(前置)→ 路由处理器 → 拦截器(后置)→ 响应
二、定义中间件
1. 类式中间件(支持依赖注入)
实现 NestMiddleware 接口的 use 方法。
import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
// 可以注入其他服务
constructor(private readonly logger: LoggerService) {}
use(req: Request, res: Response, next: NextFunction) {
console.log(`[${req.method}] ${req.url}`);
this.logger.log(`Request received`);
next(); // 必须调用以传递控制权
}
}
2. 函数式中间件(简单场景)
对于没有依赖的简单中间件,可以使用函数形式。
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
}
三、配置中间件
中间件需要在某个模块中通过 MiddlewareConsumer 进行配置,通常在模块的 configure 方法中。
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
import { LoggerMiddleware } from "./logger.middleware";
import { UsersController } from "./users.controller";
@Module({
controllers: [UsersController],
})
export class UsersModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware) // 应用中间件(可以多个)
.forRoutes("users"); // 指定路由路径(字符串或路由对象)
// 或者 forRoutes(UsersController) 应用到控制器所有路由
// 或者 forRoutes({ path: 'users', method: RequestMethod.GET })
}
}
1. 路径匹配与排除
forRoutes:可接受控制器类、路由字符串或路由对象。exclude:排除某些路径不应用中间件。
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: "users", method: RequestMethod.POST }, // 排除 POST /users
{ path: "users/(.*)", method: RequestMethod.ALL }, // 排除所有 /users/* 路由
)
.forRoutes(UsersController);
2. 应用多个中间件
apply 可以接受逗号分隔的多个中间件。
consumer.apply(cors(), helmet(), LoggerMiddleware).forRoutes("*");
四、全局中间件
如果希望中间件应用于所有路由(如 CORS、Helmet),可以在 main.ts 中直接使用 app.use()。
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { logger } from "./common/middleware/logger.middleware";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(logger); // 函数式中间件
// 或者 app.use(new LoggerMiddleware().use) 但无法注入依赖
await app.listen(3000);
}
bootstrap();
注意:全局中间件无法通过类形式获得依赖注入(除非使用 APP_MIDDLEWARE 令牌?实际上常规 app.use 不支持 DI),如果需要 DI 的全局中间件,可以创建一个模块实现 MiddlewareConsumer 并配置 * 路径,但这样每个模块都需要导入该模块。更简单的方式是使用全局守卫/拦截器代替。
五、常见用途
| 用途 | 说明 |
|---|---|
| 日志记录 | 记录每个请求的方法、URL、响应时间等 |
| 请求预处理 | 解析自定义请求头、注入请求上下文 |
| CORS | 使用 NestJS 内置 cors() 中间件(通过 app.enableCors()) |
| Helmet | 设置安全相关的 HTTP 头:app.use(helmet()) |
| 压缩 | 启用响应压缩(如 compression 中间件) |
| 限流 | 实现简单的请求频率限制(如 express-rate-limit) |
CORS 启用示例:
// main.ts
const app = await NestFactory.create(AppModule);
app.enableCors(); // 也可以传入配置对象
六、中间件与守卫、管道的区别
| 组件 | 执行时机 | 可访问内容 | 主要用途 |
|---|---|---|---|
| 中间件 | 最早(路由匹配后,守卫之前) | Request, Response | 日志、预处理、跨域等 |
| 守卫 | 中间件之后,管道之前 | ExecutionContext,可获取元数据 | 鉴权、授权 |
| 管道 | 守卫之后,控制器之前 | 参数(如 @Body 等) | 验证、转换 |
总结
- 类式中间件通过
@Injectable()支持依赖注入;函数式中间件更简单,适用于无依赖场景。 - 在模块中实现
NestModule接口,通过MiddlewareConsumer配置中间件的应用路由、排除路径。 - 全局中间件使用
app.use()注册,但无法注入依赖。 - 常用场景:日志、安全头(Helmet)、CORS、请求预处理等。
掌握中间件,可以在请求生命周期的早期阶段进行统一处理,而不侵入业务逻辑。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| NestJS 文档 | 官方 |
| Middleware | 本章主题 |
| Request lifecycle | 执行顺序 |
相关文章
认证与授权
认证(Authentication):确认「你是谁」(如 JWT、Session)。 - 授权(Authorization):确认「你能做什么」(如 RBAC、策略检查)。
缓存
@nestjs/cache-manager 统一缓存 API,存储实现可插拔。
提供者与服务
Service 是最常见的 Provider,封装业务逻辑。见 module。
守卫 (Guards) 与授权
守卫决定是否放行请求,常用于认证与授权。见 auth。
配置管理(Config 模块)
@nestjs/config 加载 .env 并提供 ConfigService。见 工程化 env。
数据库集成(以 TypeORM 为例)
Nest 通过 @nestjs/typeorm 等包集成 ORM;生产环境用 migration,慎用 synchronize。亦可选用 Prisma、MikroORM 等(见 官方 Database)。