将所有属性标记为是否需要 Joi

Mark all properties as required or not Joi

我正在构建一个 Express API 并使用 @hapi/joi 进行验证。但是我发现自己处于以下情况:如果我想验证一个新用户,模式中的所有属性都必须是必需的,但是如果客户端想要修改一个用户,这些属性必须是可选的,因为只有客户端知道什么它将修改的属性。因此,如果我为新用户提供此架构:

function validateUser(user) {
  const schema = Joi.object().keys({
    name: Joi.string().required(),
    lastName: Joi.string().required(),
    email: Joi.string().email().required(),
    password: Joi.string().required(),
    address: Joi.string().required(),
    city: Joi.string().required(),
    district: Joi.string().required()
});

  return schema.validate(user);
}

由于所有属性都是必需的,当我想使用具有相同属性但全部可选的架构时,我必须对另一个所有属性都是可选的架构进行硬编码。 Joi 有没有办法或选项来避免冗余代码?就像传递给 validateUser 函数以将其应用于模式的 isNew 参数一样?

我猜你处理问题的方式不对。架构通常应该描述您的类型在数据库中的情况。例如,如果需要名字,则无论是创建还是更新,都将始终需要它。因此,在更新的情况下,您将从数据库中获取项目并在其上应用更改,然后将其发送以进行验证。

如果你真的想这样做,你可以将上下文传递给你的验证函数,它可以使用 $ 符号从你的 joi 模式中访问。您在这里有两个选择,要么对每个 属性 应用 if else 条件,这将使您的架构看起来很丑陋,要么将两个版本的架构全部放在一起,并根据传递的上下文的值进行选择。像这样

const Joi = require("@hapi/joi");

const schemaA = Joi.object().keys({
  "name": Joi.string().required(),
  "lastName": Joi.string().required(),
  "email": Joi.string().email().required(),
  "password": Joi.string().required(),
  "address": Joi.string().required(),
  "city": Joi.string().required(),
  "district": Joi.string().required()
});

const schemaB = Joi.object().keys({
  "name": Joi.string(),
  "lastName": Joi.string(),
  "email": Joi.string().email(),
  "password": Joi.string(),
  "address": Joi.string(),
  "city": Joi.string(),
  "district": Joi.string()
});

function ChooseSchema() {
  return Joi.when(Joi.ref("$isNew"), {
    "is": true,
    "then": schemaA,
    "otherwise": schemaB
  });
}

console.log(ChooseSchema().validate({"name": "ashish"}, {"context": {"isNew": true}}));
console.log(ChooseSchema().validate({"name": "ashish"}, {"context": {"isNew": false}}));


更好的方法是使用 any.fork(paths, adjuster)

Returns 一个新架构,其中列出的每个路径键都已修改,其中:

  • paths - 键字符串数组、单个键字符串或预拆分键字符串数组的数组。键字符串路径使用点。指示密钥层次结构。
  • adjuster - 使用签名函数(模式)的函数必须 return 修改后的模式。例如,(schema) => schema.required().

该方法不修改原始架构。

例如:


// define the object

const schemaObj = {
    name: Joi.string(),
    lastName: Joi.string(),
    email: Joi.string().email(),
    password: Joi.string(),
    address: Joi.string(),
    city: Joi.string(),
    district: Joi.string()
};

// no need to use fork here every obj is already not required
const allUnreqJoiObj = Joi.object(schemaObj);

// use of fork to modify it

// fork for all keys
const allReqJoiObj = Joi.object(schemaObj).fork(Object.keys(schemaObj), (schema) => schema.required());
console.log(allReqJoiObj.validate({'city': 'some city'}));


// fork for selected keys 
const fewReqJoiObj = Joi.object(schemaObj).fork(['email', 'password'], (schema) => schema.required());
console.log(fewReqJoiObj.validate({'city': 'some city'}));

如果您已经有一个需要部分或全部键的模式,那么使用 fork 并将它们设为可选

例如:

const schemaObj = {
  name: Joi.string().required(),
  lastName: Joi.string().required(),
};

const allOptional = Joi.object(schemaObj).fork(Object.keys(schemaObj), (schema) => schema.optional());


const nameOptional = Joi.object(schemaObj).fork(['name'], (schema) => schema.optional());