前言
在本章开始之前,我们需要先了解 NestJS 的 请求管线(Request Pipeline)机制。 其核心概念包括:
- Middleware 中间件
- Guard 守卫
- Pipe 管道
- Interceptor 拦截器
- Exception Filter 异常过滤
初看可能陌生,但掌握后能让代码更优雅、易维护。
痛点:如果没有这些机制,会怎样?
假设你只用最原始的 Controller 调用:
            
            
              ts
              
              
            
          
          @Controller('user')
export class UserController {
  @Post('create')
  create(@Body() dto: CreateUserDto) {
    if (!dto.name) throw new Error('missing name');
    if (!this.authService.verify(req.headers.token)) throw new Error('unauthorized');
    const start = Date.now();
    const result = this.userService.create(dto);
    console.log('耗时', Date.now() - start);
    return { data: result };
  }
}这只是一个普通的业务逻辑,但你会发现:
- 参数验证重复
- 权限控制混杂业务逻辑
- 异常处理零散
- 日志和监控难统一
- 小改动可能破坏流程
Nest 的 "分层执行管线"
Nest 把整个请求的处理过程,拆成 一条管线(Pipeline) ,每个环节处理一种类型的问题。
这就是 Middleware → Guard → Pipe → Interceptor → Controller → Exception Filter。
| 机制 | 出发点(要解决的问题) | 执行阶段 | 典型关键词 | 失败后走向 | 
|---|---|---|---|---|
| Middleware | 路由匹配前的全局逻辑:日志、CORS、body解析、静态文件、token提取等。Express 层面的问题。 | Controller 之前(路由匹配前) | 框架层面、通用逻辑 | 中断请求 | 
| Guard | "这个请求是否被允许进入?"------访问控制与授权判断。 | Controller 之前(匹配后执行) | 权限控制、角色、token验证 | Forbidden | 
| Pipe | "请求参数是否有效?类型是否正确?"------校验与转换。 | Controller 参数解析阶段 | DTO校验、类型转换 | BadRequest | 
| Interceptor | "执行逻辑前后做些横切操作"------拦截执行、记录耗时、包裹响应。 | Controller 调用前后 | AOP、日志、响应包装 | Exception Filter | 
| Exception Filter | "错误发生了,如何优雅返回?"------统一异常格式。 | Controller 之后(异常时) | 错误捕获、返回 | 结束请求 | 
归纳一下:
- Middleware:我在门外 ------ 拦截所有请求(Express层)
- Guard:我在门口 ------ 判断是否能进当前 Controller
- Pipe:我在门槛 ------ 校验和格式化参数
- Interceptor:我在屋内 ------ 监听整个执行过程(包裹Controller)
- Exception Filter:我在出口 ------ 接住一切错误
本章我们将从管线的第一环节 Middleware 开始,详细讲解它的使用与原理。
Middleware 深入讲解
在 NestJS 的请求管线中,Middleware 是第一道防线,在路由匹配之前执行,先于 Controller,可做日志记录、CORS 处理、请求体解析、Token 提取等
Middleware 类型与使用
NestJS 的 Middleware 主要有两种类型:
1. 函数式 Middleware
- 最简单的形式,直接是一个函数。
- 不支持依赖注入。
- 使用场景:快速拦截、日志打印、简单处理请求。
            
            
              ts
              
              
            
          
          import { Request, Response, NextFunction } from 'express';
function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`[${req.method}] ${req.url}`);
  next(); // 调用 next() 将请求传给下一个 Middleware 或 Controller
}
// 全局注册
app.use(logger);2. 类式 Middleware(推荐)
- 实现 NestMiddleware 接口。
- 支持依赖注入(可以通过 constructor 注入 Service)。
- 使用场景:业务相关的预处理、模块化逻辑。
            
            
              ts
              
              
            
          
          import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { AuthService } from './auth.service';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(private authService: AuthService) {}
  use(req: Request, res: Response, next: NextFunction) {
    const token = req.headers['authorization'];
    if (!this.authService.verify(token)) {
      return res.status(401).json({ message: 'Unauthorized' });
    }
    next();
  }
}注册方式
1. 全局 Middleware
            
            
              ts
              
              
            
          
          // main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
function logger(req, res, next) {
  console.log(`[${req.method}] ${req.url}`);
  next();
}
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 全局注册函数式 middleware
  app.use(logger);
  await app.listen(3000);
}
bootstrap();2. 模块级 Middleware
            
            
              ts
              
              
            
          
          // user.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { UserController } from './user.controller';
import { AuthMiddleware } from './auth.middleware';
@Module({
  controllers: [UserController],
})
export class UserModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(AuthMiddleware)       // 指定中间件
      .forRoutes(UserController);  // 模块级:拦截 UserController 内所有路由
  }
}3. 路由级 Middleware
            
            
              ts
              
              
            
          
          // user.module.ts
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { UserController } from './user.controller';
import { LoggerMiddleware } from './logger.middleware';
@Module({
  controllers: [UserController],
})
export class UserModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(
        { path: 'user/create', method: RequestMethod.POST }, // 仅拦截 POST /user/create
        { path: 'user/:id', method: RequestMethod.GET },     // 仅拦截 GET /user/:id
      );
  }
}小结:
| 类型 | 注册方式 | 拦截范围 | 适用场景 | 
|---|---|---|---|
| 全局 Middleware | app.use(fn) | 应用内所有路由 | 日志、CORS、全局鉴权 | 
| 模块级 Middleware | consumer.apply(...).forRoutes(Controller) | 模块内所有路由 | 模块统一逻辑,支持 DI | 
| 路由级 Middleware | consumer.apply(...).forRoutes({ path, method }) | 指定接口 | 精确拦截特定路由或方法 | 
Express middleware
为什么要讲 Express middleware?
在讲解 NestJS 的中间件之前,我们必须先理解 Express 的中间件模型。
原因在于:
- 
Nest 默认底层是基于 Express 框架构建 的(除非手动切换为 Fastify 适配器); 
- 
因此,Nest 的中间件机制本质上就是 对 Express middleware 的封装与模块化抽象; 
- 
理解 Express middleware 的执行时机、参数形式和调用链机制,有助于我们更清楚 Nest 的内部工作原理。 
换句话说,Nest 的 Middleware 是"更结构化的 Express Middleware" 。
如果你知道 app.use() 是如何工作的,你就能立刻明白 @MiddlewareConsumer().apply() 背后的逻辑。
Express middleware 的使用
在 Express 中,中间件(Middleware)是一个函数,它能访问 请求对象 (req) 、响应对象 (res) 和 下一个中间件函数 (next) 。
定义中间件Logger 如下:
            
            
              ts
              
              
            
          
          function logger(req, res, next) {
  console.log(`Request... ${req.method} ${req.url}`);
  next(); // 调用下一个中间件,否则请求会被挂起
}- 用法一:全局中间件
            
            
              ts
              
              
            
          
          const express = require('express');
const app = express(); 
app.use(logger); // 全局中间件
app.get('/users', (req, res) => res.send('User list'));
app.listen(3000);- 用法二:匹配 /users*的请求
            
            
              ts
              
              
            
          
          const express = require('express');
const app = express(); 
app.use('/users', logger); // 只作用于具体某个路径
app.get('/users', (req, res) => res.send('User list'));
app.listen(3000);- 用法三:匹配 router模块
            
            
              ts
              
              
            
          
          // admin.module.js
const express = require('express');
const adminRouter = express.Router();
const { adminAuthMiddleware } = require('./admin.middleware');
adminRouter.use(adminAuthMiddleware);
adminRouter.get('/dashboard', (req, res) => res.send('Admin Dashboard'));
adminRouter.get('/settings', (req, res) => res.send('Admin Settings'));
module.exports = { adminRouter }
// user.module.js
const userRouter = express.Router();
const { userLoggerMiddleware } = require('./user.middleware');
userRouter.use(userLoggerMiddleware);
userRouter.get('/profile', (req, res) => res.send('User Profile'));
userRouter.get('/settings', (req, res) => res.send('User Settings'));
module.exports = { userRouter }
// app.js
const express = require('express');
const app = express();
const { adminRouter } = require('./admin.module');
const { userRouter } = require('./user.module');
app.use('/admin', adminRouter); // admin 模块带 auth 中间件,且admin模块的路由前缀为/admin
app.use('/user', userRouter);   // user 模块带 logger 中间件,且user模块的路由前缀为/userExpress 中间件是整个框架的灵魂,它形成了一个"洋葱模型(Onion Model) "的请求管线,所有逻辑都在这个链上依次流转。
Nestjs middleware实现原理
其实 Nest 没有重新发明中间件,而是在 Express 机制上叠加了模块系统和依赖注入能力。
这一章不再重复依赖注入的实现逻辑了,从下面可以看到源码中最终将中间件挂载在底层httpServer(express)上是通过 app.use(path, middlewareFunction) 来实现的。
            
            
              ts
              
              
            
          
            private async registerHandler(
    applicationRef: HttpServer,
    routeInfo: RouteInfo,
    proxy: <TRequest, TResponse>(
      req: TRequest,
      res: TResponse,
      next: () => void,
    ) => void,
  ) {
    const { method } = routeInfo;
    const paths = this.routeInfoPathExtractor.extractPathsFrom(routeInfo);
    const isMethodAll = isRequestMethodAll(method);
    const requestMethod = RequestMethod[method];
    // 这里的 createMiddlewareFactory 返回的是 app.use, 如果是在解析router的过程中则返回的是 controller 里的method.
    const router = await applicationRef.createMiddlewareFactory(method);
    const middlewareFunction = isMethodAll
      ? proxy
      : <TRequest, TResponse>(
          req: TRequest,
          res: TResponse,
          next: () => void,
        ) => {
          if (applicationRef.getRequestMethod?.(req) === requestMethod) {
            return proxy(req, res, next);
          }
          return next();
        };
    const pathsToApplyMiddleware = [] as string[];
    paths.some(path => path.match(/^\/?$/))
      ? pathsToApplyMiddleware.push('/')
      : pathsToApplyMiddleware.push(...paths);
    pathsToApplyMiddleware.forEach(path => router(path, middlewareFunction));
  }使用 Nest Middleware 时有哪些注意点:
- 
异步中间件支持度: Express 原生并不直接支持异步中间件,常用做法如下: tsconst asyncHandler = fn => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); }; app.use(asyncHandler(async (req, res, next) => { await someAsyncTask(); next(); }));而 Nest 允许 middleware 写成 async use(req, res, next)。 
- 
中间件执行顺序: Express中,中间件的执行顺序就是注册顺序; 但在Nest中,它不直接暴露 app.use() 顺序,而是遵循以下三层规则👇: - 
规则 1:在同一个模块中,中间件的顺序 = consumer.apply() 调用顺序 tsexport class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) // ✅ 第 1 个执行 .forRoutes('*'); consumer .apply(AuthMiddleware) // ✅ 第 2 个执行 .forRoutes('admin'); } }
- 
规则 2:跨模块时,顺序由模块的加载顺序决定 假设你的模块结构如下,每个模块都实现了自己的中间件: ts@Module({ imports: [AuthModule, LoggerModule], }) export class AppModule {}那么请求时执行顺序是: AuthMiddleware → LoggerMiddleware → Controller 
- 
规则 3:全局中间件最先执行 如果你通过 app.use() 注册中间件(即在 main.ts 中写): tsconst app = await NestFactory.create(AppModule); app.use(cors()); app.use(helmet());那么这些会在所有模块中间件之前执行。 
 
- 
- 
错误捕获: - Middleware 内抛出的异常如果不捕获,会交给 Nest 的 Exception Filter
 
总结:
NestJS 的 Middleware 是请求管线的第一道环节,用于框架层面的请求前处理(如日志、CORS、body解析、Token 提取等)。它基于底层 HTTP 框架(Express / Fastify),并对中间件进行了模块化封装,支持依赖注入和路由级控制,从而在大型项目中更灵活和可维护。
Middleware 的源码主要在 这里,之所以没有花很多篇幅去讲这个文件的实现,是觉得源码涉及细节较多,但对理解核心原理不是必须;如果后面有需要,会再细化这方面的知识点。