NestJS中@UserId装饰器获取ID返回NaN错误求助
报错信息
15556 - 29.09.2023, 20:19:14 ERROR [ExceptionsHandler] invalid input syntax for type integer: "NaN"
问题现象
- 未添加
@ApiBearerAuth()时,调用GET /profile接口仅返回第一个用户的信息; - 添加
@ApiBearerAuth()后,触发上述报错,控制台打印的id值为NaN。
相关代码
参数装饰器(TypeScript)
export const UserId = createParamDecorator( (data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); return request.user?.id ? Number(request.user.id) : null; }, );
控制器代码
@Controller('users') @ApiTags('users') @ApiBearerAuth() export class UsersController { constructor(private readonly usersService: UsersService) {} @Get('/profile') @UseGuards(JwtAuthGuard) getProfile(@UserId() id: number) { console.log(id) return this.usersService.findById(id); } }
Service方法
async findById(id: number) { return this.repository.findOneBy({ id, }); }
原因分析
装饰器类型转换逻辑缺陷
装饰器中request.user?.id ? Number(request.user.id) : null的判断存在问题:- 若
request.user.id是0、空字符串这类假值,会直接返回null; - 若
request.user.id本身是无法转换为数字的内容(比如非数字字符串、undefined),Number()会返回NaN,最终传入Service层导致数据库报错。
- 若
JWT解析后用户ID格式异常
添加@ApiBearerAuth()后,请求会携带token并经过JwtAuthGuard验证,若JWT payload中的id不是有效数字类型(比如是字符串、或者解析后为undefined),就会导致Number()转换失败生成NaN。未授权时的查询逻辑异常
未添加@ApiBearerAuth()时,JwtAuthGuard可能未生效(比如Swagger未携带token,请求直接绕过验证),此时request.user不存在,装饰器返回null。数据库执行findOneBy({id: null})时,部分数据库会将null视为忽略该条件,返回第一条数据,这就是为什么只能看到第一个用户的信息。
解决方案
修正装饰器的类型转换逻辑
先判断用户ID是否存在,再验证转换后的数字有效性:export const UserId = createParamDecorator( (data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); const userId = request.user?.id; if (userId == null) return null; const numId = Number(userId); // 转换失败返回null或直接抛出错误 return isNaN(numId) ? null : numId; }, );校验JWT解析结果
检查JwtAuthGuard的逻辑,确保解析token后request.user.id是有效的数字类型(比如在生成JWT时,将id存为数字而非字符串)。添加参数校验
在控制器或Service层增加参数校验,避免无效值传入数据库:getProfile(@UserId() id: number) { if (isNaN(id)) { throw new BadRequestException('无效的用户ID'); } console.log(id) return this.usersService.findById(id); }确保接口授权校验生效
确认JwtAuthGuard在未携带有效token时会拦截请求,避免未授权访问导致的异常查询。
内容的提问来源于stack exchange,提问作者Максим Дмитриевич

