如何复用 Joi Schema 的属性定义而不继承验证规则
发布时间 - 2026-01-05 00:00:00 点击率:次本文介绍在 joi 中如何安全复用基础 schema 的字段定义,避免意外继承 `.xor()`、`.messages()` 等链式配置,通过 `object.keys()` 方法实现纯净的属性扩展。
Joi 的 Schema 实例是不可变(immutable)的,每次调用 .xor()、.messages()、.requiredKeys() 等方法都会返回一个全新 Schema 实例,而非修改原对象。这意味着:直接对已配置 .xor() 和 .messages() 的 Schema 调用 .keys() 或 .append(),不会污染原始定义,但若你从一个“已增强”的 Schema 出发(如 baseS
chema.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.',
});⚠️ 注意事项:
- ❌ 避免 const baseSchema = Joi.object({...}).xor(...).messages(...); const reused = baseSchema.keys({...}); —— 此时 reused 仍含 xor 和 messages;
- ✅ baseFields 必须是 纯 Joi.object() 实例,不带任何链式修饰;
- .keys() 是浅合并:若传入同名键(如 a),会完全覆盖原定义;如需条件性扩展,可先用 .describe() 提取结构,或借助 Joi.compile() 动态构建;
- Joi v17+ 中 .keys() 已取代旧版 .add(),语义更清晰,推荐统一使用。
总结:Joi 的复用核心在于分层设计——将字段声明(schema structure)与业务规则(validation logic + messages)解耦。baseFields 扮演「类型契约」角色,而各 .xor() + .messages() 组合则作为面向具体场景的「验证策略」。这种模式不仅提升可维护性,也天然支持单元测试中对字段定义的独立断言。
# app
# red
# Object
# const
# 继承
# append
# 对象
# 链式
# 复用
# 而非
# 如需
# 中对
# 先用
# 第二步
# 旧版
# 仍会
# 若你
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
香港服务器建站指南:免备案优势与SEO优化技巧全解析
Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】
HTML 中如何正确使用模板变量为元素的 name 属性赋值
Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】
HTML透明颜色代码在Angular里怎么设置_Angular透明颜色使用指南【详解】
网站制作大概要多少钱一个,做一个平台网站大概多少钱?
javascript读取文本节点方法小结
昵图网官网入口 昵图网素材平台官方入口
网站制作大概多少钱一个,做一个平台网站大概多少钱?
Firefox Developer Edition开发者版本入口
利用vue写todolist单页应用
个人摄影网站制作流程,摄影爱好者都去什么网站?
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
阿里云网站搭建费用解析:服务器价格与建站成本优化指南
Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】
java ZXing生成二维码及条码实例分享
使用C语言编写圣诞表白程序
微信小程序 require机制详解及实例代码
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
详解vue.js组件化开发实践
Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全
Laravel如何配置和使用缓存?(Redis代码示例)
Laravel如何实现API速率限制?(Rate Limiting教程)
如何在HTML表单中获取用户输入并用JavaScript动态控制复利计算循环
javascript如何操作浏览器历史记录_怎样实现无刷新导航
Linux安全能力提升路径_长期防护思维说明【指导】
PHP怎么接收前端传的文件路径_处理文件路径参数接收方法【汇总】
进行网站优化必须要坚持的四大原则
Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】
焦点电影公司作品,电影焦点结局是什么?
Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权
ChatGPT回答中断怎么办 引导AI继续输出完整内容的方法
如何快速配置高效服务器建站软件?
JavaScript模板引擎Template.js使用详解
深圳网站制作培训,深圳哪些招聘网站比较好?
长沙企业网站制作哪家好,长沙水业集团官方网站?
Laravel如何使用withoutEvents方法临时禁用模型事件
浅谈javascript alert和confirm的美化
Laravel如何实现事件和监听器?(Event & Listener实战)
MySQL查询结果复制到新表的方法(更新、插入)
如何在云主机快速搭建网站站点?
HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】
Laravel定时任务怎么设置_Laravel Crontab调度器配置
网站视频制作书签怎么做,ie浏览器怎么将网站固定在书签工具栏?
如何在阿里云虚拟主机上快速搭建个人网站?
如何快速搭建自助建站会员专属系统?
独立制作一个网站多少钱,建立网站需要花多少钱?
Laravel Blade组件怎么用_Laravel可复用视图组件的创建与使用
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
如何在宝塔面板中修改默认建站目录?

