有没有办法合并 Joi 模式?
Posted
技术标签:
【中文标题】有没有办法合并 Joi 模式?【英文标题】:Is There A Way To Merge Joi Schemas? 【发布时间】:2017-08-12 09:32:32 【问题描述】:有没有办法将两个 joi 模式合并为一个模式?
架构 1
alpha: Joi.number().required(),
beta: Joi.string().required(),
chalie: Joi.object(
xray: Joi.number().required(),
).required()
架构 1
delta: Joi.string().required(),
echo: Joi.number().required(),
charlie: Joi.object(
zulu: Joi.string().required(),
).required()
合并架构:
alpha: Joi.number().required(),
beta: Joi.string().required(),
chalie: Joi.object(
xray: Joi.number().required(),
zulu: Joi.string().required(),
).required()
delta: Joi.string().required(),
echo: Joi.number().required(),
如果没有嵌套对象,使用Object.assign
可以轻松完成,但即使是深度对象合并也无法处理嵌套对象,因为嵌套对象是一个函数调用。
【问题讨论】:
您可以使用Object.assign()
和Joi
的concat()
的组合来创建自己的函数
【参考方案1】:
我想知道同样的事情,因为我想合并两个不同的模式,发现了这个:https://github.com/hapijs/joi/blob/v9.0.4/API.md#anyconcatschema
const a = Joi.string().valid('a');
const b = Joi.string().valid('b');
const ab = a.concat(b);
希望对你有帮助
【讨论】:
【参考方案2】:你尝试过 Joi.append 吗?
https://github.com/hapijs/joi/blob/v13.5.2/API.md#objectkeysschema
// Validate key a
const base = Joi.object().keys(
a: Joi.number()
);
// Validate keys a, b.
const extended = base.append(
b: Joi.string()
);
更新(2020-05-03):
一个简单的方法是这样的:
var base = Joi.object( firstname: Joi.string() );
var fullName = base.keys( lastName: Joi.number() );
【讨论】:
如果您尝试附加架构,则会导致AssertionError [ERR_ASSERTION]: Object schema cannot be a joi schema
【参考方案3】:
使用普通的 javascript 对象对我来说不是一个选项。我尝试使用.keys
方法进行扩展,但它覆盖了现有的键(在这种情况下是用于 charlie)。
我选择的解决方案是使用.reach
:
示例:
const Joi = require('joi');
const originalSchema = Joi.object(
a:
deep:
b: Joi.string()
,
c: Joi.string()
);
const extendedSchema = Joi.object(
a:
deep: Joi
.reach(originalSchema, 'a.deep')
.keys( anotherB: Joi.string() )
,
c: Joi.reach(originalSchema, 'c')
);
// No errors
extendedSchema.validate( a: deep: b: 'hi', anotherB: 'hi' , c: 'wow' )
【讨论】:
【参考方案4】:我不喜欢这里的任何答案,所以我找到了另一种方法。我创建了一个类,这样我就可以为一个项目设置一个规则,比如一个电子邮件地址或密码,它具有单一的需求来源,而不是不同文件中的多个模式。甚至是单个文件/类中的多个半冗余模式。
值得注意的是,如果第一条规则为空,.append 将不起作用。这就是 .concat 的用武之地。
首先我建立了一个包含几个单项规则的类
//an email address
static emailAddress = Joi.object(
emailAddress: Joi.string()
.email( tlds: allow: false )
.required()
.label("Email Address"),
);
static passwordRegex = /^(?=.*[A-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])\S8,$/;
static passwordError =
"Password must be at least 8 characters long, and have at least one uppercase letter, one lowercase letter, one number, and one special character.";
//a password
static password = Joi.object(
password: Joi.string()
.min(8)
.regex(this.passwordRegex)
.message(this.passwordError)
.label("Password"),
);
然后我为需要检查的特定对象创建了一些规则。
static registerUserSchema()
let schema = Joi.object()
.concat(this.yourNameSchema)
.concat(this.emailAddress)
.concat(this.password)
.concat(this.confirmPassword);
return schema;
我花了很长时间才弄清楚,但这完美无缺。
【讨论】:
这应该算是正确答案【参考方案5】:Joi.object()
和扩展运算符 ...
为我解决了问题。 (Joi 版本 17)
import * as Joi from 'joi'
const childSchema:
PORT: Joi.number(),
const parentSchema = Joi.object(
NODE_ENV: Joi.string(),
APP_NAME: Joi.string(),
...childSchema,
)
【讨论】:
【参考方案6】:这是@szanata answer的扩展,这是一个合并用于验证请求正文的多个模式的工作示例。我将它创建为路由的中间件,有时最多有 3 个模式来验证请求正文。您可以传递单个架构或架构数组。
const validateRequest = (schema) =>
return (req, res, next) =>
if(Array.isArray(schema))
let schemas = schema;
schema = schemas[0]
schemas.forEach((s, idx) =>
if (idx > 0) schema = schema.concat(s);
);
let data = ...req.body, ...req.query, ...req.params ;
const error = schema.validate(data, options);
if (error) res.status(422).json(msg:`$error.details.map((x) => x.message).join(", ")`)
else next();
路由中间件的用法示例:
const query, mongoDbId, test = require("../utils/validation-schema");
const router = express.Router();
router.post("/test", protect, validateInput([mongoDbId, test, query]),
(req, res) =>
res.json( msg: "OK" );
);
concat后console.log(schema._ids)的输出。
_byId: Map ,
_byKey: Map
'_id' => schema: [Object], id: '_id' ,
'databaseType' => schema: [Object], id: 'databaseType' ,
'host' => schema: [Object], id: 'host' ,
'database' => schema: [Object], id: 'database' ,
'user' => schema: [Object], id: 'user' ,
'password' => schema: [Object], id: 'password' ,
'datasource' => schema: [Object], id: 'datasource' ,
'sql' => schema: [Object], id: 'sql' ,
'modifier' => schema: [Object], id: 'modifier' ,
'sqlType' => schema: [Object], id: 'sqlType' ,
'format' => schema: [Object], id: 'format' ,
'timeout' => schema: [Object], id: 'timeout'
,
_schemaChain: false
【讨论】:
不错的解决方案,您可以通过使用reduce
:const concatenatedSchemas = schema.reduce((a,b) => a.concat(b), Joi.any())
来简化连接代码【参考方案7】:
虽然您可以使用 Javascript 的 Object.assign()
,但我认为您正在寻找的是 Joi 的 .keys()
函数。
在你的代码中,我会这样做:
const schema1 = Joi.object(
alpha: Joi.number().required(),
beta: Joi.string().required(),
charlie: Joi.object(
xray: Joi.number().required(),
).required()
);
const schema2 = Joi.object(
delta: Joi.string().required(),
echo: Joi.number().required(),
charlie: Joi.object(
zulu: Joi.string().required(),
).required()
);
const mergedSchema = schema1.keys(schema2);
还有一个 interesting note 关于使用直接 JS 对象与将它们包装在 Joi.object()
中;
使用 表示法时,您只是定义了一个普通的 JS 对象,它不是架构对象。您可以将其传递给验证方法,但不能调用对象的 validate() 方法,因为它只是一个普通的 JS 对象。
此外,每次将 对象传递给 validate() 方法,每次验证都会执行昂贵的架构编译操作。
当您使用 Joi.object([schema]) 时,它会在第一次编译,因此您可以多次将其传递给 validate() 方法,并且不会增加任何开销。
所以你可以接受Ankh's 的建议并使用直接的 JS 对象:
const schema1 =
alpha: Joi.number().required(),
beta: Joi.string().required(),
charlie: Joi.object(
xray: Joi.number().required(),
).required()
;
const schema2 =
delta: Joi.string().required(),
echo: Joi.number().required(),
charlie: Joi.object(
zulu: Joi.string().required(),
).required()
;
const mergedSchema = Object.assign(, schema1, schema2);
但存在相关的性能损失。
【讨论】:
【参考方案8】:https://github.com/hapijs/joi/blob/v15.0.1/API.md#objectappendschema
object.append([schema]) 附加允许的对象键,其中:
schema - 可选对象,其中每个键都分配有一个 joi 类型对象。如果 schema 为 null、undefined 或 ,则不会应用任何更改。使用 object.keys([schema]) 附加键。
// Validate key a const base = Joi.object().keys( a: Joi.number() ); // Validate keys a, b. const extended = base.append( b: Joi.string() );
【讨论】:
@Undistraction 它实际上是来自文档的复制粘贴:github.com/hapijs/joi/blob/v15.0.1/API.md#objectappendschema 不适用于 joi 版本 17,它返回“错误:对象架构不能是 joi 架构”以上是关于有没有办法合并 Joi 模式?的主要内容,如果未能解决你的问题,请参考以下文章