有没有办法合并 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()Joiconcat() 的组合来创建自己的函数 【参考方案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 模式?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法合并多个单声道错误信号?

有没有办法可以防止虚线边框合并?

有没有办法干净轻松地撤消 git 合并?

有没有办法合并不同的来源分支?

UITest 代码覆盖率报告 - 有没有办法合并多个 .xccovreport?

有没有办法在行和列索引上合并熊猫数据框?