【问题标题】:Is There A Way To Merge Joi Schemas?有没有办法合并 Joi 模式?
【发布时间】: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() 的组合来创建自己的函数

标签: validation object merge joi


【解决方案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])(?=.*[!@#$%^&*()])\S{8,}$/;
        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()
          });
          

          【讨论】:

          猜你喜欢
          • 2017-08-20
          • 1970-01-01
          • 2019-08-30
          • 2016-08-29
          • 2017-06-22
          • 2017-12-11
          • 2015-01-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多