

新闻资讯
技术学院本文介绍在 joi 中如何安全复用基础 schema 的字段定义,避免意外继承 `.xor()`、`.messages()` 等链式配置,通过 `object.keys()` 方法实现纯净的属性扩展。
Joi 的 Schema 实例是不可变(immutable)的,每次调用 .xor()、.messages()、.requiredKeys() 等方法都会返回一个全新 Schema 实例,而非修改原对象。这意味着:直接对已配置 .xor() 和 .messages() 的 Schema 调用 .keys() 或 .append(),不会污染原始定义,但若你从一个“已增强”的 Schema 出发(如 baseSchema.xor(...).messages(...)),再调用 .keys(),新 Schema 仍会携带之前所有规则 —— 这正是问题根源。
✅ 正确做法是:仅对“纯净”的基础 Schema(即仅含 .object({ ... }) 定义、未附加任何校验逻辑或消息)调用 .keys()。
以下为推荐实践:
const Joi = require('@hapi/joi'); // 或 @joi/browser(v17+)
// ✅ 第一步:定义纯净的基础字段结构(无 xor / messages)
const baseFields = Joi.object({
a: Joi.string().trim().empty(null, ''),
b: Joi.string().guid().empty(null),
});
// ✅ 第二步:基于 baseFields 构建不同用途的完整 Schema
const firstSchema = baseFields
.xor('a', 'b')
.messages({
'object.missing': 'One of "a", "b" is required.',
'object.xor': 'Only one of "a", "b" is allowed.',
});
const extendedSchema = baseFields.keys({
c: Joi.string().trim(),
});
const secondSchema = extendedSchema
.xor('a', 'b', 'c')
.messages({
'object.missing': 'One of "a", "b", "c" is required.',
'object.xor': 'Only one of "a", "b", "c" is allowed.',
});⚠️ 注意事项:
总结:Joi 的复用核心在于分层设计——将字段声明(schema structur
e)与业务规则(validation logic + messages)解耦。baseFields 扮演「类型契约」角色,而各 .xor() + .messages() 组合则作为面向具体场景的「验证策略」。这种模式不仅提升可维护性,也天然支持单元测试中对字段定义的独立断言。