有没有办法合并Joi Schemas?

时间:2017-03-20 22:09:25

标签: validation object merge joi

有没有办法将两个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轻松完成,但即使深层对象合并也不能与嵌套对象一起使用,因为嵌套对象是一个函数调用。

8 个答案:

答案 0 :(得分:5)

使用普通的javascript对象不是我的选择。我尝试使用.keys方法进行扩展,但它会覆盖现有密钥(在本例中为查理)。

我确定的解决方案是使用.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' })

答案 1 :(得分:1)

您尝试过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()
});

答案 2 :(得分:1)

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,
})

答案 3 :(得分:1)

这是@szanata 答案的扩展,这是合并用于验证请求正文的多个模式的工作示例。我将它创建为路由的中间件,有时最多有 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
}

答案 4 :(得分:0)

我想知道同一件事,因为我想合并两个不同的模式,并发现了这一点: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);

希望对您有帮助

答案 5 :(得分:0)

我不喜欢这里的任何答案,所以我找到了另一种方法。我创建了一个类,以便可以为一个项目设置一条规则,例如电子邮件地址或密码,该规则具有针对需求的单一起点,而不是在不同文件中设置多个架构。甚至单个文件/类中的多个半冗余架构。

值得一提的是,如果第一个规则为空,则.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;
  }

永远带我去弄清楚,但这无可挑剔。

答案 6 :(得分:-1)

虽然您可以使用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);

但是存在相关的性能损失。

答案 7 :(得分:-2)

https://github.com/hapijs/joi/blob/v15.0.1/API.md#objectappendschema

  

object.append([schema])   在以下位置附加允许的对象键:

     

schema-可选对象,其中为每个键分配了一个joi类型的对象。如果架构为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()
});