Joi验证中间件无法获取req.body请求体问题排查
问题描述
我有一个用于验证user对象的中间件,在用户创建和编辑时生效。不久前它还能正常工作,但今早修改后发现失效了,找不到原因。
排查后确认:中间件函数已被调用(通过console.log测试),但从中间件中获取的req.body为undefined。
Joi Schema
const Joi = require('joi'); module.exports.userSchema = Joi.object({ user: Joi.object({ username: Joi.string().min(2).max(40).required(), password: Joi.string().min(8).max(15).required(), email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }).required(), location: Joi.string().alphanum(), image: Joi.string().alphanum(), website: Joi.string().uri({ scheme: [ /https?/ ] }), stripeId: Joi.string().alphanum(), }) });
中间件函数
const { userSchema } = require('./schemas.js'); const ExpressError = require('./utils/ExpressError'); module.exports.validateUser = (req, res, next) => { const { error } = userSchema.validate(req.body); console.log("validation schema running"); // 输出`validation schema running` console.log(req.body) // 输出`undefined` if (error) { const msg = error.details.map(el => el.message).join(',') throw new ExpressError(msg, 400) } else { next(); } }
路由配置
const { validateUser } = require('../middleware'); router.route('/:id') .get( isLoggedIn, isUser, catchAsync(users.showUser)) .put( validateUser, isLoggedIn, upload.array('image'), catchAsync(users.updateUser))
控制器函数
module.exports.updateUser = async (req, res) => { const { id } = req.params; console.log("req body ", req.body); const user = await User.findByIdAndUpdate(id, { ...req.body.user }); const imgs = req.files.map(f => ({ url: f.path, filename: f.filename })); user.image.push(...imgs); await user.save(); res.redirect(`/users/${user._id}`); };
控制器函数中能正常获取req.body:
req body [Object: null prototype] { user: [Object: null prototype] { username: "Dan's Sands", location: '1 Main St, Austin, TX 78703', email: 'test@test.com', website: 'https://test.com' } }
补充:服务器请求体解析逻辑(App.js)
app.use(bodyParser.raw({type: "application/json"})); app.use(express.json({ limit: '1mb' })); app.use(express.urlencoded({ extended: true }));
为什么请求体无法传递到我的中间件函数中?
问题原因与解决办法
核心原因:中间件顺序错误 + 解析器冲突
1. 路由中间件顺序搞反了
你的PUT路由里,validateUser放在了upload.array('image')前面。但upload(应该是multer这类文件上传中间件)才是负责解析带文件的表单数据、填充req.body的关键。
因为你当前的请求是multipart/form-data类型(包含文件上传),express.json()和express.urlencoded()都处理不了这种格式,只有multer的upload中间件能解析。所以validateUser先执行时,req.body还没被解析,自然是undefined;而upload之后的控制器就能拿到正确的req.body。
2. App.js里的解析器冗余冲突
你同时用了bodyParser.raw({type: "application/json"})和express.json(),这俩都是解析JSON请求体的中间件,重复设置可能导致解析异常。而且对于multipart请求,这俩完全不起作用。
解决步骤
- 调整路由中间件顺序
把validateUser挪到upload.array('image')后面,确保req.body被multer解析后再做验证:
router.route('/:id') .get( isLoggedIn, isUser, catchAsync(users.showUser)) .put( isLoggedIn, upload.array('image'), validateUser, catchAsync(users.updateUser))
- 清理App.js的冗余解析器
删掉重复的JSON解析中间件,保留必要的即可:
// 移除这行,express.json()已经包含相同功能 // app.use(bodyParser.raw({type: "application/json"})); app.use(express.json({ limit: '1mb' })); app.use(express.urlencoded({ extended: true }));
- 修正Joi Schema的小问题(可选)
你的Schema里password设为必填,但控制器收到的请求体里没有password字段,验证会失败。如果编辑用户时不需要强制输入密码,可以把password改为可选:
password: Joi.string().min(8).max(15), // 去掉.required()
内容的提问来源于stack exchange,提问作者econobro
相关产品推荐
相关产品推荐

