2025/03/01
NestJS AOP
深入理解 NestJS AOP 面向切面编程的核心概念与实战应用
MVC 架构流程
在 MVC 架构下,请求都会发送给 Controller,由它来调度 Model 层的 service,完成对应的业务逻辑:
Controller → Model [ Service、Repository ] → View
AOP(面向切面编程)
NestJS 提供了 AOP(Aspect Oriented Programming,面向切面编程) 的能力。
什么是 AOP?
请求发过来,会经过 Controller(控制器)、Service(服务)、Repository(数据库)访问的逻辑。如果想在这个逻辑中加入一些通用型的代码,比如在 Controller 调用之前和之后加入一个执行通用逻辑的阶段,就需要使用 AOP。
一、Middleware(中间件)
1.1 全局中间件
// main.ts
import {NestFactory} from '@nestjs/core'
import {NextFunction} from 'express'
import {AppModule} from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.use((req: Request, res: Response, next: NextFunction) => {
console.log('before')
next()
console.log('after')
})
await app.listen(process.env.PORT ?? 3000)
}
bootstrap()
访问示例输出:
// before
// after
Handler 代码:
import {Controller, Get} from '@nestjs/common'
import {AppService} from './app.service'
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {
}
@Get()
getHello(): string {
console.log('handler...')
return this.appService.getHello()
}
@Get('aaa')
aaa() {
console.log('aaa')
return 'aaa'
}
@Get('bbb')
bbb() {
console.log('bbb')
return 'bbb'
}
}
输出结果:
// before /
// handler...
// after
// before /aaa
// aaa
// after
// before /bbb
// bbb
// after
1.2 路由中间件
除了全局中间件,NestJS 还支持路由中间件。
生成中间件:
nest g middleware log --no-spec --flat
| 参数 | 说明 |
|---|---|
--no-spec | 不生成单元测试文件 |
--flat | 不生成单独的目录 |
配置路由中间件:
// app.module.ts
import {MiddlewareConsumer, Module, NestModule} from '@nestjs/common'
import {AppController} from './app.controller'
import {AppService} from './app.service'
import {LogMiddleware} from './log.middleware'
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LogMiddleware).forRoutes('aaa*')
}
}
访问结果:
// before /bbb
// bbb
// after
// before /aaa
// before middleware
// aaa
// after middleware
// after
只有在访问
aaa开头的路由时,中间件才会生效。
二、Guard(守卫)
Guard 是路由守卫的意思,可以在调用 Controller 之前判断权限,返回 true 或 false 来决定是否放行。
生成 Guard:
nest g guard login --no-spec --flat
使用示例:
import {Controller, Get, UseGuards} from '@nestjs/common'
@Controller()
export class AppController {
@Get('aaa')
@UseGuards(LoginGuard) // 使用权限判断
aaa() {
console.log('aaa')
return 'aaa'
}
}
2.1 全局 Guard
像中间件一样,Guard 也可以全局使用:
// main.ts
import {LoginGuard} from './login.guard'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.use((req: Request, res: Response, next: NextFunction) => {
console.log('before', req.url)
next()
console.log('after')
})
app.useGlobalGuards(new LoginGuard())
await app.listen(process.env.PORT ?? 3000)
}
三、Interceptor(拦截器)
拦截器可以在目标 Controller 方法后加入一些逻辑。
生成 Interceptor:
nest g interceptor time --no-spec --flat
使用方式和 Guard 差不多。
3.1 Interceptor 与 Guard 的区别
| 特性 | Guard | Interceptor | Middleware |
|---|---|---|---|
| Metadata 支持 | ✅ 支持 | ✅ 支持 | ❌ 不支持 |
| 作用范围 | 路由/全局 | 路由/全局 | 路由/全局 |
| 放行时机 | Controller 之前 | Controller 之后/响应前 | 请求最外层 |
注意:
- 可以在 Controller 和 Handler 上添加 Metadata,这种数据只能在 Interceptor 或 Guard 里取出,Middleware 不行
- 支持在每个路由单独启用,只作用于某一个 Handler
四、Pipe(管道)
Pipe 主要用于参数验证和转换。
生成 Pipe:
nest g pipe validate --no-spec --flat
4.1 自定义参数验证 Pipe
// validate.pipe.ts
import {
ArgumentMetadata,
BadRequestException,
Injectable,
PipeTransform,
} from '@nestjs/common'
@Injectable()
export class ValidatePipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
// 这里的 value 就是传入的参数
// 如果不能转成数字,就返回参数错误,否则乘 10 再传入 handler
if (Number.isNaN(Number.parseInt(value))) {
throw new BadRequestException(`参数 ${metadata.data} 错误`)
}
return typeof value === 'number' ? value * 10 : Number.parseInt(value) * 10
}
}
使用示例:
import {Controller, Get, Query} from '@nestjs/common'
import {ValidatePipe} from './validate.pipe'
@Controller()
export class AppController {
@Get('ccc')
ccc(@Query('num', ValidatePipe) num: number) {
return num + 1
}
}
效果: 当参数错误时,会返回 400 的错误响应。
4.2 内置 Pipe
NestJS 提供了多种内置 Pipe:
| Pipe | 功能 |
|---|---|
ValidationPipe | 数据验证 |
ParseIntPipe | 转换为整数 |
ParseFloatPipe | 转换为浮点数 |
ParseBoolPipe | 转换为布尔值 |
ParseArrayPipe | 转换为数组 |
ParseUUIDPipe | 验证 UUID |
ParseEnumPipe | 验证枚举值 |
Pipe 同样可以对某个参数生效,也能对全局生效。
五、ExceptionFilter(异常过滤器)
NestJS 提供了 ExceptionFilter 来支持对抛出的异常做统一处理。
例如 throw new BadRequestException() 就是使用了异常处理。
生成 ExceptionFilter:
nest g filter test --no-spec --flat
使用示例:
// test.filter.ts
import {
ArgumentsHost,
BadRequestException,
Catch,
ExceptionFilter,
} from '@nestjs/common'
import {Response} from 'express'
@Catch(BadRequestException)
export class TestFilter implements ExceptionFilter {
catch(exception: BadRequestException, host: ArgumentsHost) {
const response: Response = host.switchToHttp().getResponse()
response.status(400).json({
statusCode: 400,
message: `400${exception.message}`,
})
}
}
效果: 这样处理后,异常项返回的响应就统一被格式化了。
AOP 执行顺序总结
Request
↓
Middleware (中间件)
↓
Guard (守卫) → 返回 false 则阻断
↓
Interceptor (拦截器) - Before
↓
Pipe (管道) - 参数验证/转换
↓
Controller Handler
↓
Interceptor (拦截器) - After
↓
ExceptionFilter (异常过滤器) - 如有异常
↓
Response
组件对比表
| 组件 | 主要作用 | 执行时机 |
|---|---|---|
| Middleware | 请求预处理、日志记录等 | 请求最外层 |
| Guard | 权限验证 | Controller 之前 |
| Interceptor | 响应转换、日志记录等 | Controller 前后 |
| Pipe | 参数验证、转换 | Handler 调用前 |
| ExceptionFilter | 统一异常处理 | 发生异常时 |