自定义装饰器
用 SetMetadata 与 createParamDecorator 复用元数据与参数提取。
NestJS 内置了大量装饰器(@Get、@Post、@Body、@Req 等),但有时需要创建自己的装饰器来复用通用逻辑或提取特定数据。自定义装饰器可以分为参数装饰器和方法/类装饰器两类。
一、自定义参数装饰器(createParamDecorator)
参数装饰器用于从请求中提取特定数据,比如当前登录用户、分页参数、请求头中的某字段。
使用 createParamDecorator 工厂函数创建,它接收两个参数:
data:装饰器传入的静态参数(可选)。ctx:ExecutionContext对象,可以获取请求实例。
示例:提取当前用户
假设在认证守卫中已经将用户信息挂载到 request.user,我们可以创建一个 @CurrentUser() 装饰器直接获取:
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
export const CurrentUser = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user; // 返回用户对象
});
在控制器中使用:
@Get('profile')
getProfile(@CurrentUser() user: UserEntity) {
return user;
}
传递参数给装饰器:createParamDecorator 的第一个参数 data 就是调用时传入的值。
export const CurrentUser = createParamDecorator(
(key: string, ctx: ExecutionContext) => {
const user = ctx.switchToHttp().getRequest().user;
return key ? user?.[key] : user;
},
);
// 使用
@Get()
getUserEmail(@CurrentUser('email') email: string) { ... }
二、结合管道使用
自定义参数装饰器可以像内置参数装饰器(@Body、@Query)一样与管道配合使用。只需在参数声明时添加管道即可。
@Get(':id')
findOne(
@Param('id', ParseIntPipe) id: number,
@CurrentUser(ValidationPipe) user: UserEntity,
) { ... }
管道会在装饰器提取数据之后执行,对提取的结果进行验证或转换。
三、组合装饰器(applyDecorators)
当需要将多个装饰器组合成一个时,可以使用 applyDecorators 函数。它接受多个装饰器并返回一个组合装饰器。
示例:创建一个 @Public() 装饰器,用于标记公开路由(跳过认证守卫)
通常我们会通过 @SetMetadata 设置元数据,然后守卫读取该元数据。可以封装成更语义化的装饰器:
import { SetMetadata, applyDecorators } from "@nestjs/common";
export const Public = () =>
applyDecorators(
SetMetadata("isPublic", true),
// 还可以组合其他装饰器,如 @HttpCode(200) 等
);
然后在控制器方法上使用 @Public(),守卫中检查 isPublic 元数据即可。
批量组合示例:创建一个 @ApiPaginatedResponse 装饰器,组合 Swagger 装饰器。
import { applyDecorators, Type } from "@nestjs/common";
import { ApiOkResponse, getSchemaPath } from "@nestjs/swagger";
export function ApiPaginatedResponse<T extends Type<any>>(model: T) {
return applyDecorators(
ApiOkResponse({
schema: {
allOf: [
{ properties: { data: { type: "array", items: { $ref: getSchemaPath(model) } } } },
{ properties: { total: { type: "number" } } },
],
},
}),
);
}
四、自定义方法/类装饰器(基于 SetMetadata 与反射)
除了参数装饰器,还可以为方法或类创建装饰器,通常会结合 SetMetadata 设置元数据,然后在守卫或拦截器中通过 Reflector 读取。
1. 直接使用 SetMetadata
@SetMetadata('roles', ['admin'])
@Get()
findAll() { ... }
2. 封装为自定义装饰器
import { SetMetadata } from "@nestjs/common";
export const Roles = (...roles: string[]) => SetMetadata("roles", roles);
使用:
@Roles('admin', 'moderator')
@Delete(':id')
remove(@Param('id') id: number) { ... }
3. 组合多个元数据
import { applyDecorators, SetMetadata } from "@nestjs/common";
export const Auth = (role: string) =>
applyDecorators(SetMetadata("role", role), SetMetadata("requiresAuth", true));
然后在守卫中通过 Reflector 读取:
const role = this.reflector.get<string>("role", context.getHandler());
const requiresAuth = this.reflector.get<boolean>("requiresAuth", context.getHandler());
4. 更复杂的类装饰器
可以通过 @Injectable() 或自定义类装饰器来实现类似继承的功能,但更常见的是用元数据 + 守卫。
五、总结
| 类型 | 创建方式 | 典型用途 |
|---|---|---|
| 参数装饰器 | createParamDecorator | 提取请求数据(用户、分页、设备信息) |
| 组合装饰器 | applyDecorators | 聚合多个装饰器,简化代码 |
| 方法/类装饰器 | SetMetadata + Reflector | 设置权限、公开路由等元数据 |
自定义装饰器是 NestJS 中非常强大的扩展能力,能帮助你将横切关注点从控制器中抽离,使代码更简洁、可维护。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| NestJS 文档 | 官方 |
| Custom decorators | 本章主题 |
| 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)。
Series
new
7 / 19