Nest
里使用到了装饰器decorators
。装饰器在程序语言里是非常被人熟知的,但是在JavaScript
领域,它还是相对较新的。为了能够更好的理解装饰器,推荐读一下这篇文章,下面是它简单定义描述:
ES2016装饰器是一个返回函数的表达式,可以将目标、名称和属性描述符作为参数。通过在装饰符前面加上@字符并将其放在要装饰的内容的最顶端来应用它。可以为类、方法或属性定义修饰符。
参数装饰器
Nest提供一些有用的参数装饰器您可以在Http路由里使用他们。下面是装饰器相对Exporess(或者 fastify)简单的对应表
装饰器 | 对应 |
---|---|
@Request(), @Req() | req |
@Response(), @Res() | res |
@Next() | next |
@Session() | req.session |
@Param(param?: string) | req.params / req.params[param] |
@Body(param?: string) | req.body / req.body[param] |
@Query(param?: string) | req.query / req.query[param] |
@Headers(param?: string) | req.headers / req.headers[param] |
@Ip() | req.ip |
@HostParam() | req.hosts |
另外,您可以定义您自己的装饰器。为什么他们有用? 在Node.js的世界,通常将属性附加到请求对象。然后使用如下代码在每个路由处理程序中手动提取它们:
const user = req.user;
为了使您的代码更具可读性,您可以创建@User
装饰器然后在您的控制器Controllers
上利用它。
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
然后,您可以在任何你想用的地方用它。
@Get()
async findOne(@User() user: UserEntity) {
console.log(user);
}
传递数据
当装饰器的行为取决于某些条件时,可以使用数据参数将参数传递给装饰器的工厂函数。其中一个用例是自定义装饰器,它通过键从请求对象中提取参数。例如,假设我们的身份验证层验证请求并将用户实体附加到请求对象。经过身份验证的请求的用户实体可能如下所示:
{
"id": 101,
"firstName": "Alan",
"lastName": "Turing",
"email": "alan@email.com",
"roles": ["admin"]
}
让我定义装饰器拿属性参数作为key
,如果存在的话则返回相应的值(如果不存在则返回undefined,或者user
对象还没有被创建)
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;
},
);
下面是在controller
中你怎么通过@user()
来访问特定的属性
@Get()
async findOneByName(@User('firstName') firstName: string) {
console.log(`Hello ${firstName}`);
}
您可以利用不同的key的相同的装饰器来取得不同的属性。如果user
又深又复杂,这可以使request
请求参数变得更简单更易阅读。
注意
对于TypeScript用户,请注意createParamDecorator<T>()是一个泛型。这意味着您可以显式地强制类型安全,例如createParamDecorator<string>((data,ctx)=>…)。或者,在工厂函数中指定参数类型,例如createParamDecorator((data:string,ctx)=>…)。如果省略这两个,数据的类型将为any。
和管道一起工作
Nest
对待自定义参数装饰器的方式和内置装饰器(@Body()
,@Param()
和@Query()
)相同。这意味着也为自定义装饰器可作为执行管道的参数(在我们的示例中为用户参数)。此外,可以将管道直接应用于自定义装饰器:
@Get('c')
async findOnePipe(
@User(new ValidationPipe({ validateCustomDecorators: true }))
user: UserEntity,
) {
console.log(user);
}
注意validateCustomDecorators选项必须设置为true。默认情况下,ValidationPipe不验证用自定义修饰符注释的参数。
装饰器组合
Nest
提供的帮助方法来组合多个装饰器。比如:您想要把多个认证的装饰器整合到一个装饰器内。可以参照下面的做法。
import { applyDecorators } from '@nestjs/common';
export function Auth(...roles: Role[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(AuthGuard, RolesGuard),
ApiBearerAuth(),
ApiUnauthorizedResponse({ description: 'Unauthorized' }),
);
}
然后您就能使用@Auth()装饰器了
@Get('users')
@Auth('admin')
findAllUsers() {}
这具有用一个声明应用所有四个装饰器的效果。
警告
@nestjs/swagger包中的@ApiHideProperty()装饰器不可组合,无法与applyDecorators函数一起正常工作。