自定义装饰器 ( Custom decorators )
我们在守卫 (guards) 章节中,曾通过Reflector#createDecorator
方法创建了一个@Roles()
装饰器。
Nest 是围绕一种称为装饰器的语言功能构建的。虽然装饰器是许多常用编程语言中的一个众所周知的概念,但是在JavaScript 中,仍然是一个相对较新的功能。
下方是一个简单的定义:
An ES2016 decorator is an expression which returns a function and can take a target, name and property descriptor as arguments. You apply it by prefixing the decorator with an @ character and placing this at the very top of what you are trying to decorate. Decorators can be defined for either a class, a method or a property.
参数装饰器
Nest 提供了一组有用的参数装饰器,你可以将它们与 HTTP 路由处理程序一起使用。下面是提供的装饰器和它们代表的普通 Express(或 Fastify)对象的列表:
此外,你可以创建自己的自定义装饰器。
你可能会问,为什么这会有用呢?
在Node.js
中,将属性附加到请求对象是常见的做法。然后在每个路由处理程序中手动提取它们,使用如下代码:
const user = req.user;
为了使你的代码更具可读性和透明性,你可以创建一个@User()
装饰器 并在所有控制器中重复使用它。
/* user.decorator.ts */
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
}
)
然后,你可以在任何符合你要求的地方简单地使用它:
/* example */
@Get()
async findOne(@User() user: UserEntity) {
console.log(user);
}
传递数据
当装饰器的行为取决于某些条件时,可以使用data
参数将参数传递给装饰器的工厂函数。比如:我们可以自定义一个装饰器,它通过键从请求对象中提取属性。例如,假设我们的认证层验证请求并将用户实体附加到请求对象。经过身份验证的请求的用户实体可能如下所示:
{
"id": 101,
"firstName": "Alan",
"lastName": "Turing",
"email": "alan@email.com",
"roles": ["admin"]
}
让我们来定义一个装饰器,它以属性名称作为键,如果存在则返回关联值 (如果不存在则返回 undefined,或者尚未创建user
对象) 。
在上方user.decorator.ts
代码的基础之上:
/* user.decorator.ts */
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
export const User = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
}
)
下方代码是如何通过控制器中的@User()
装饰器访问特定属性:
/* example */
@Get()
async findOne(@User('firstName') firstName: string) {
console.log(`Hello, ${firstName}!`);
}
你可以使用具有不同键的相同装饰器来访问不同的属性。如果user
对象很深或很复杂,这种方法可以使请求处理程序的实现更容易、更易读。
使用管道
Nest 会以与内置装饰器 (@Body()
、@Param()
、@Query()
) 相同的方式处理自定义参数装饰器。这意味着管道也会针对自定义注释参数执行 (示例中为user
参数) 。此外,你可以将管道直接应用于自定义装饰器:
@Get()
async findOne(
@User(new ValidationPipe({ validateCustomDecorators: true }))
user: UserEntity,
) {
console.log(user);
}
注意:validateCustomDecorators
选项必须设置为true
。默认情况下,ValidationPipe
不会验证使用自定义装饰器注释的参数。
装饰器组成 (composition)
Nest 提供了一个辅助方法来结合多个装饰器。例如,你想要将所有与身份验证相关的装饰器组合成一个装饰器。这可以通过如下代码实现:
/* auth.decorator.ts */
import { applyDecorators, SetMetadata, UseGuards } from "@nestjs/common";
import { AuthGuard } from "src/auth.guard";
import { RolesGuard } from "src/common/guard/roles.guard";
export function Auth(...roles: []) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(AuthGuard, RolesGuard),
);
}
然后,你可以按照如下方式来使用此自定义的@Auth
装饰器:
/* example */
@Get('users')
@Auth('admin')
findAllUsers() {}
这具有通过单个声明应用所有两个装饰器的效果。
总结
至此,我们已经将Nest 比较基本入门的知识点给掌握了!
如果对于基础知识还有感兴趣的朋友,还可以前往NestJS 中文官方网站前去查阅知识哟~
如果你是第一次刷到我对应的NestJS 文章,你可以通过我的主页寻找第一篇NestJS 新手入门的文章开始阅读起。