Auth Decorator
在使用了 @nestjs/passport 进行用户登录验证后,我们时长需要一个装饰器来限制用户需要登录。
import { applyDecorators, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
export function Auth() {
return applyDecorators(UseGuards(AuthGuard('jwt')));
}
当前登录用户
在登录后,我们时常需要获取当前登录用户的信息。这时候就可以配合之前的 AuthDecorator 来创建一个参数装饰器获取当前用户了
import { createParamDecorator } from '@nestjs/common';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
export const CurrentUser = createParamDecorator((data: unknown, ctx: ExecutionContextHost) => {
const request = ctx.switchToHttp().getRequest();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return request.user;
});
合并路由参数
我们可能需要将 ID 等参数信息合并到 Body 中,并且自动转换为 number 类型
import { BadRequestException, createParamDecorator, ExecutionContext } from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate, ValidationError } from 'class-validator';
/**
* 将路由参数合并到 Body 中,然后进行验证
* 用于在编辑场景中自动将 id 等参数合并到 DTO,使验证器能够正确工作
*
* @example
* @Patch(':id')
* update(
* @Param('id', ParseIntPipe) id: number,
* @BodyWithParams(UpdateUserDto) updateDto: UpdateUserDto,
* ) {
* // updateDto.id 已经包含了路由参数中的 id (已转换为 number)
* return this.userService.update(id, updateDto);
* }
*/
export const BodyWithParams = createParamDecorator(async (dtoClass: any, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const body = request.body;
const params = request.params;
// 合并 params 和 body (params 中的值优先,避免被篡改)
const merged = { ...body, ...params };
// 转换参数类型(id 从 string 转为 number)
if (merged.id && typeof merged.id === 'string') {
merged.id = Number.parseInt(merged.id, 10);
}
// 转换为 DTO 实例
const dtoInstance = plainToInstance(dtoClass, merged);
// 执行验证
const errors = await validate(dtoInstance);
if (errors.length > 0) {
// 格式化错误信息,保持与 FieldValidationPipe 一致的格式
const formatErrors = (errs: ValidationError[]): any[] => {
return errs.map(err => ({
field: err.property,
message: Object.values(err.constraints || {}),
}));
};
throw new BadRequestException(formatErrors(errors));
}
return dtoInstance;
});