Joi Schema配置问题:POST需id必填、PUT需禁用id字段
Joi Schema 适配POST/PUT请求的字段规则问题解决
问题场景
现有如下Joi Schema定义:
const commonFields = { id: joi.string().required(), name: joi.string().required().min(5).max(50), nif: joi.string().length(9).required(), diet: joi.bool().required().strict(), vegetarian: joi.bool().required().strict(), }; const aluno = joi.object({ ...commonFields, num: joi .string() .required() .regex(/^\d{1,4}\/\d{2}$/), regime: joi.string().required().valid("externo", "interno"), });
需要实现两个规则:
- POST请求创建用户时,
id字段为必填项 - PUT请求更新用户时,请求体中禁止出现
id字段(id从req.params.id获取)
尝试用alter()方法配置字段规则,并在验证时传入请求方法上下文,但测试PUT请求时,请求体带id并未触发预期的"id" is not allowed错误。
问题原因
alter()方法的规则分支不是通过context参数触发的,它需要显式调用.tailor()方法指定要应用的分支名,之前的代码仅传入了context但未触发分支切换,导致规则未生效。
正确解决方案
方案1:使用alter() + .tailor()
保持alter()的规则配置,验证时通过.tailor()指定对应分支:
- 更新
commonFields中的id字段:
const commonFields = { id: joi.string().alter({ post: (schema) => schema.required(), put: (schema) => schema.forbidden() }), name: joi.string().required().min(5).max(50), nif: joi.string().length(9).required(), diet: joi.bool().required().strict(), vegetarian: joi.bool().required().strict(), };
- 验证逻辑中切换分支:
async function updateUser(req, res, type, db) { try { // 根据请求方法选择对应的alter分支,注意转小写匹配分支名 const schema = requestValidation[type].tailor(req.method.toLowerCase()); const { error } = schema.validate(req.body); if (error) { return res.status(400).send({ error: error.message }); } const id = req.params.id; const { name, nif, vegetarian, diet } = req.body; // ...后续业务逻辑 } catch (err) { // ...错误处理 } }
方案2:拆分独立Schema(更直观)
如果觉得分支逻辑不够清晰,可以直接为POST和PUT分别定义独立Schema:
// 抽离基础字段,不含id的规则 const baseFields = { name: joi.string().required().min(5).max(50), nif: joi.string().length(9).required(), diet: joi.bool().required().strict(), vegetarian: joi.bool().required().strict(), }; // POST创建用户的Schema:id必填 const alunoPostSchema = joi.object({ ...baseFields, id: joi.string().required(), num: joi.string().required().regex(/^\d{1,4}\/\d{2}$/), regime: joi.string().required().valid("externo", "interno"), }); // PUT更新用户的Schema:id禁止出现在请求体 const alunoPutSchema = joi.object({ ...baseFields, id: joi.string().forbidden(), num: joi.string().required().regex(/^\d{1,4}\/\d{2}$/), regime: joi.string().required().valid("externo", "interno"), });
验证时直接根据请求方法选择对应Schema:
async function updateUser(req, res, type, db) { try { const schema = req.method === 'POST' ? alunoPostSchema : alunoPutSchema; const { error } = schema.validate(req.body); if (error) { return res.status(400).send({ error: error.message }); } const id = req.params.id; const { name, nif, vegetarian, diet } = req.body; // ...后续业务逻辑 } catch (err) { // ...错误处理 } }
额外注意事项
- 确保验证时未开启
allowUnknown: true,否则Joi会忽略请求体中的未知字段,不会触发forbidden()的错误提示。 - PUT请求中如果需要验证
req.params.id的格式,可以单独添加参数验证逻辑:
const paramsSchema = joi.object({ id: joi.string().required() }); const paramsError = paramsSchema.validate(req.params).error; if (paramsError) { return res.status(400).send({ error: paramsError.message }); }
内容的提问来源于stack exchange,提问作者palmutska
相关产品推荐
相关产品推荐

