NestJS中如何正确使用mongoose.isValidObjectId作为校验中间件
问题根因
你的中间件失效有两个核心原因:
- 第一,
mongoose.isValidObjectId本身存在校验漏洞:它仅判断入参能否被强制转换为ObjectId类型,不会校验转换后的值是否和原始传入值一致。你报错场景里的id629b9fbd620dbc419a52e8长度不符合合法MongoDB ObjectId的24位十六进制规则,但isValidObjectId会误判为合法,直到后续查询数据库执行类型转换时才抛出CastError。 - 第二,中间件路由匹配规则写法错误:
forRoutes('/')仅会匹配根路径请求,不会匹配orders模块下诸如/orders/:id这类带参数的子路由,导致中间件根本没在对应接口上执行。
正确实现方案
1. 修正ObjectId校验逻辑
修改validateMongoID.ts的校验判断,增加转换后等值校验,彻底拦截非法格式id,同时兼容不同命名的id类参数:
import { BadRequestException, Injectable, NestMiddleware, } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import mongoose from 'mongoose'; @Injectable() export class IsValidObjectId implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { // 自动识别所有名称带id的路径参数,无需硬编码单个参数名 const idParams = Object.keys(req.params).filter(key => key.toLowerCase().includes('id')); for (const paramKey of idParams) { const idValue = req.params[paramKey]; // 双重校验:先判断是否可转换,再判断转换后值和原值完全一致 if (!mongoose.isValidObjectId(idValue) || String(new mongoose.Types.ObjectId(idValue)) !== idValue) { throw new BadRequestException(`Invalid ${paramKey} format`); } } next(); } }
2. 修正中间件绑定规则
不要写死根路径,直接绑定到Orders模块下的控制器,避免路径前缀变更导致匹配失效:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { IsValidObjectId } from './validateMongoID.middleware'; import { OrdersController } from './orders.controller'; import { OrdersService } from './orders.service'; @Module({ controllers: [OrdersController], providers: [OrdersService] }) export class OrdersModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(IsValidObjectId) // 直接绑定控制器,覆盖该控制器下所有路由 .forRoutes(OrdersController); } }
可选优化方案
如果不想给模块下所有路由加校验,可以用Nest原生的参数级Pipe实现按需复用,比中间件更灵活:
import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; import mongoose from 'mongoose'; @Injectable() export class ParseObjectIdPipe implements PipeTransform { transform(value: string) { if (!mongoose.isValidObjectId(value) || String(new mongoose.Types.ObjectId(value)) !== value) { throw new BadRequestException('Invalid ObjectId format'); } return value; } }
使用时直接在控制器参数上声明即可,无需在模块中注册中间件:
@Get(':id') findOne(@Param('id', ParseObjectIdPipe) id: string) { return this.ordersService.findOne(id); }
内容的提问来源于stack exchange,提问作者Dolan
相关产品推荐
相关产品推荐

