16

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

4

8 回答 8

13

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

希望对你有帮助

于 2019-06-25T20:42:13.603 回答
9

你试过 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() });
于 2018-08-07T23:45:55.503 回答
6

使用普通的 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' })
于 2017-07-03T23:02:04.240 回答
1

这是@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
}
于 2021-02-02T08:50:24.540 回答
1

我不喜欢这里的任何答案,所以我找到了另一种方法。我创建了一个类,这样我就可以为一个项目设置一个规则,比如一个电子邮件地址或密码,它具有单一的需求来源,而不是不同文件中的多个模式。甚至单个文件/类中的多个半冗余模式。

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

我花了很长时间才弄清楚,但这完美无瑕。

于 2020-08-02T22:33:43.890 回答
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,
})
于 2021-01-05T16:35:33.463 回答
-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);

还有一个关于使用直接 JS 对象与包装它们的有趣说明Joi.object()

使用 {} 表示法时,您只是定义了一个普通的 JS 对象,它不是架构对象。您可以将其传递给验证方法,但不能调用对象的 validate() 方法,因为它只是一个普通的 JS 对象。

此外,每次将 {} 对象传递给 validate() 方法,都会对每次验证执行昂贵的架构编译操作。

当您使用 Joi.object([schema]) 时,它会在第一次被编译,因此您可以将它多次传递给 validate() 方法,并且不会增加任何开销。

所以你可以接受Ankh 的建议并使用直接的 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);

但有一个相关的性能损失。

于 2017-05-31T19:33:07.323 回答
-2

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()
});
于 2019-05-07T14:10:21.070 回答