【问题标题】:Mark all properties as required or not Joi将所有属性标记为是否需要 Joi
【发布时间】:2020-06-20 00:19:57
【问题描述】:

我正在构建一个 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 参数一样?

【问题讨论】:

    标签: node.js express joi


    【解决方案1】:

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

    返回一个新模式,其中列出的每个路径键都已被修改,其中:

    • paths - 键字符串数组、单个键字符串或预拆分键字符串数组。键字符串路径使用点。表示密钥层次结构。
    • adjuster - 一个使用签名函数(模式)的函数,它必须返回修改后的模式。例如,(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());
    
    

    【讨论】:

      【解决方案2】:

      我猜你以错误的方式解决问题。 Schema 通常应该描述您的类型在 DB 中的情况。例如,如果需要名字,则无论是创建还是更新,都将始终需要它。因此,在更新的情况下,您将从 db 中获取项目并在此之上应用更改,然后将其发送以进行验证。

      如果您真的想这样做,您可以将上下文传递给您的验证函数,该函数可以使用$ 符号从您的 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}}));
      
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-16
        • 1970-01-01
        • 2015-09-20
        • 1970-01-01
        • 2011-08-20
        • 1970-01-01
        相关资源
        最近更新 更多