Nestjs中间件常见使用方式(class、函数中间件)
'# Nestjs中间件常见使用方式(class、函数中间件)
一、背景与问题
在构建复杂的Node.js应用时,中间件是实现请求处理流程的核心组件。Nestjs作为基于TypeScript的渐进式Node.js框架,提供了两种中间件实现方式:函数式中间件和基于类的中间件。这两种实现方式在底层原理上存在本质差异,但在实际开发中各有适用场景。
当前开发中常见的中间件使用问题包括:
- 中间件执行顺序理解错误导致逻辑混乱
- 未正确处理异常导致程序崩溃
- 未考虑性能影响造成请求延迟
- 安全性配置不当暴露敏感信息
- 依赖注入失效导致代码耦合
二、基本原理
1. 函数式中间件原理
函数式中间件是通过use方法注册的普通函数,其执行流程如下:
function logger(req: Request, res: Response, next: Function) {
console.log(`Request: ${req.method} ${req.url}`);
next();
}底层实现通过fastify或express的中间件机制,将请求处理流程组织为链式调用。每个中间件函数接收三个参数:请求对象、响应对象和next函数。
2. 类中间件原理
类中间件通过@Injectable()装饰器注册,其执行流程如下:
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log(`Request: ${req.method} ${req.url}`);
next();
}
}底层通过@nestjs/common模块的中间件系统,将类方法注册为中间件实例。类中间件支持依赖注入和装饰器,可以更灵活地组织业务逻辑。
3. 中间件执行顺序
Nestjs中间件的执行顺序遵循以下规则:
- 与路由绑定的中间件按声明顺序执行
- 全局中间件在路由中间件之前执行
@UseFilters装饰器注册的异常处理中间件在最后执行
三、环境准备
创建Nestjs项目:
npm i -g @nestjs/cli
nest new nest-middleware-demo
cd nest-middleware-demo
npm install项目结构:
src/
├── main.ts
├── app.controller.ts
├── app.module.ts
├── middleware/
│ ├── logger.middleware.ts
│ └── auth.middleware.ts
└── common/
└── filters/
└── http-exception.filter.ts四、核心实现
1. 函数式中间件实现
// src/middleware/logger.middleware.ts
export function loggerMiddleware(req: Request, res: Response, next: Function) {
console.log(`Request: ${req.method} ${req.url}`);
next();
}在路由中使用:
// src/app.controller.ts
import { Controller, Get, UseMiddleware } from '@nestjs/common';
import { loggerMiddleware } from '../middleware/logger.middleware';
@Controller()
export class AppController {
@Get()
@UseMiddleware(loggerMiddleware)
getHello(): string {
return 'Hello World';
}
}关键点说明:
- 中间件函数必须接受三个参数
next()函数调用控制流程继续- 中间件可以修改请求/响应对象
2. 类中间件实现
// src/middleware/logger.middleware.ts
import { Injectable } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`Request: ${req.method} ${req.url}`);
next();
}
}在路由中使用:
// src/app.controller.ts
import { Controller, Get, UseMiddleware } from '@nestjs/common';
import { LoggerMiddleware } from '../middleware/logger.middleware';
@Controller()
export class AppController {
@Get()
@UseMiddleware(LoggerMiddleware)
getHello(): string {
return 'Hello World';
}
}关键点说明:
- 使用
@Injectable()进行依赖注入 - 支持装饰器和类型校验
- 更适合复杂业务逻辑处理
3. 异常处理中间件
// src/common/filters/http-exception.filter.ts
import { ExceptionFilter, Catch, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: any) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response.status(status).json({
message: exception.message,
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}在模块中注册:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
@Module({
imports: [],
providers: [HttpExceptionFilter],
controllers: [],
})
export class AppModule {}关键点说明:
- 通过
@Catch装饰器捕获异常 - 支持自定义异常处理逻辑
- 适合统一错误处理
五、完整案例
构建一个用户认证系统:
// src/middleware/auth.middleware.ts
import { Injectable } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class AuthMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const token = req.headers['authorization'];
if (!token || token !== 'secret-token') {
throw new HttpException('Unauthorized', 401);
}
next();
}
}// src/app.controller.ts
import { Controller, Get, UseMiddleware } from '@nestjs/common';
import { AuthMiddleware } from './middleware/auth.middleware';
@Controller()
export class AppController {
@Get()
@UseMiddleware(AuthMiddleware)
getHello(): string {
return 'Hello World';
}
}// src/common/filters/http-exception.filter.ts
import { ExceptionFilter, Catch, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: any) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response.status(status).json({
message: exception.message,
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}// src/app.module.ts
import { Module } from '@nestjs/common';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
@Module({
imports: [],
providers: [HttpExceptionFilter],
controllers: [],
})
export class AppModule {}运行测试:
npm run start访问 http://localhost:3000 时会返回 401 Unauthorized 错误,而使用正确 token 时返回正常响应。
六、源码解析
以类中间件的执行流程为例,源码中关键部分如下:
// @nestjs/common/src/middleware/middleware.ts
export class NestMiddleware {
// 中间件注册逻辑
static registerMiddleware(
app: FastifyInstance,
middleware: NestMiddleware,
): void {
const middlewares = app.middlewares;
middlewares.push(middleware);
}
// 中间件执行逻辑
static applyMiddlewares(
req: Request,
res: Response,
next: Function,
middlewares: NestMiddleware[],
): void {
const executeMiddleware = (index: number) => {
if (index >= middlewares.length) {
return next();
}
const middleware = middlewares[index];
if (middleware instanceof Function) {
middleware(req, res, () => executeMiddleware(index + 1));
} else {
middleware.use(req, res, () => executeMiddleware(index + 1));
}
};
executeMiddleware(0);
}
}关键点分析:
- 中间件注册采用链式调用方式
- 支持函数式和类中间件的统一处理
- 通过递归方式执行中间件链
七、进阶使用
1. 中间件链式调用
@UseMiddlewares(LoggerMiddleware, AuthMiddleware)
getHello(): string {
return 'Hello World';
}2. 中间件装饰器组合
@UsePipes(new ValidationPipe())
@UseInterceptors(new LoggingInterceptor())3. 中间件参数注入
@Injectable()
export class ConfigMiddleware {
constructor(private readonly configService: ConfigService) {}
use(req: Request, res: Response, next: Function) {
console.log(this.configService.get('APP_NAME'));
next();
}
}八、性能与工程实践
1. 性能优化策略
- 中间件顺序优化:将耗时中间件放在最后
- 避免不必要的中间件调用
- 使用缓存中间件处理重复请求
- 对关键中间件进行性能测试
2. 异常处理最佳实践
- 为每个中间件单独处理异常
- 使用
@Catch装饰器统一处理 - 避免在中间件中直接throw异常
3. 安全性考虑
- 中间件不应暴露敏感信息
- 对敏感中间件进行加密处理
- 使用
@UseFilters装饰器统一处理异常
4. 代码组织建议
- 将中间件按功能分类组织
- 使用
@Injectable()进行依赖注入 - 对复杂中间件进行单元测试
九、常见问题与踩坑
1. 中间件顺序错误
错误示例:
@UseMiddleware(AuthMiddleware, LoggerMiddleware)问题:认证中间件应该在日志中间件之前执行
正确顺序:
@UseMiddleware(LoggerMiddleware, AuthMiddleware)2. 未正确处理异常
错误示例:
throw new HttpException('...', 401);问题:未使用@Catch装饰器处理异常
正确方式:
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
// ...
}3. 中间件未注册
错误示例:
@UseMiddleware(LoggerMiddleware)问题:未在模块中注册中间件
正确方式:
import { LoggerMiddleware } from './middleware/logger.middleware';
@Module({
providers: [LoggerMiddleware],
// ...
})4. 未正确处理请求参数
错误示例:
req.headers['authorization'] // 未处理undefined情况改进方式:
const token = req.headers['authorization'] || '';十、最佳实践
类中间件推荐场景:
- 需要依赖注入时
- 逻辑复杂需要拆分时
- 需要装饰器支持时
- 需要类型校验时
函数中间件推荐场景:
- 简单逻辑处理时
- 不需要依赖注入时
- 需要快速实现时
中间件使用规范:
- 所有中间件必须使用
@Injectable()装饰器 - 异常处理必须使用
@Catch装饰器 - 中间件必须使用
use方法 - 中间件顺序必须符合业务逻辑
- 所有中间件必须使用
性能优化建议:
- 对关键中间件进行缓存
- 避免不必要的中间件调用
- 使用性能分析工具进行优化
- 对中间件进行单元测试
十一、总结
Nestjs中间件作为请求处理的核心组件,其合理使用对系统性能和可维护性至关重要。通过对比函数式中间件和类中间件的实现方式,我们可以发现:
- 类中间件更适合复杂业务逻辑
- 函数中间件适合简单处理逻辑
- 中间件顺序直接影响执行流程
- 异常处理是必须考虑的部分
- 安全性需要特别注意
在实际开发中,建议遵循以下原则:
- 根据业务需求选择合适的中间件类型
- 保持中间件逻辑的单一职责
- 合理组织中间件的执行顺序
- 对关键中间件进行性能测试
- 使用统一的异常处理机制
通过合理使用中间件,可以显著提升系统的可维护性、可扩展性和安全性。在大型项目中,建议建立中间件管理规范,确保团队成员的代码质量和开发效率。
评论已关闭