【问题标题】:Nested Validations With FolktaleFolktale 的嵌套验证
【发布时间】:2018-06-18 20:27:44
【问题描述】:

我一直在一个新项目中使用FolktaleValidation,我发现它非常有用,但我遇到了需要顺序验证的难题。我有一个配置对象,我需要执行以下验证:

  • is 是一个对象吗?
  • 对象的密钥是否有效(它们是否出现在白名单中)?
  • 键的值是否有效?

每次验证都依赖于之前的验证 - 如果项目不是对象,验证其键是没有意义的(并且会出错),如果对象没有键,验证它们的值是没有意义的。实际上,如果验证失败,我想短路验证。

我最初的想法是使用Result,而不是在其他地方定义和使用的Validatio, but mixing the two types feels confusing, and I already havevalidateIsObject`。

我目前(工作但丑陋)的解决方案在这里:

import { validation } from 'folktale';
import { validateIsObject } from 'folktale-validations';
import validateConfigKeys from './validateConfigKeys';
import validateConfigValues from './validateConfigValues';

const { Success, Failure } = validation;

export default config => {
  const wasObject = validateIsObject(config);
  let errorMessages;
  if (Success.hasInstance(wasObject)) {
    const hadValidKeys = validateConfigKeys(config);
    if (Success.hasInstance(hadValidKeys)) {
      const hasValidValues = validateConfigValues(config);
      if (Success.hasInstance(hasValidValues)) {
        return Success(config);
      }
      errorMessages = hasValidValues.value;
    } else {
      errorMessages = hadValidKeys.value;
    }
  } else {
    errorMessages = wasObject.value;
  }
  return Failure(errorMessages);
};

我最初采用嵌套matchWiths 的方法,但这更难阅读。

如何改进此解决方案?

【问题讨论】:

    标签: javascript validation functional-programming folktale


    【解决方案1】:

    您可以编写一个帮助程序来应用验证规则,直到返回 Failure。一个简单的例子:

    const validateUntilFailure = (rules) => (x) => rules.reduce(
      (result, rule) => Success.hasInstance(result) 
        ? result.concat(rule(x)) 
        : result,
      Success()
    );
    

    我们使用concat 来组合两个结果。我们使用Success.hasInstance 来检查是否需要应用下一条规则。你的模块现在只有一行:

    export default config => validateUntilFailure([ 
      validateIsObject, validateConfigKeys, validateConfigValues
    ]);
    

    请注意,一旦看到Failure,此实现就不会提前返回。递归实现可能是更实用的方法,但不会吸引所有人:

    const validateUntilFailure = ([rule, ...rules], x, result = Success()) => 
      Failure.hasInstance(result) || !rule
        ? result
        : validateUntilFailure(rules, x, result.concat(rule(x)))
    

    查看下面的示例以了解运行代码。有一个部分被注释掉,显示如何运行所有规则,即使有失败。

    const { Success, Failure } = folktale.validation;
    
    const validateIsObject = (x) =>
      x !== null && x.constructor === Object
      	? Success(x)
    	  : Failure(['Input is not an object']);
    
    const validateHasRightKeys = (x) =>
      ["a", "b"].every(k => k in x) 
      	?  Success(x)
    		:  Failure(['Item does not have a & b.']);
    
    const validateHasRightValues = (x) =>
      x.a < x.b
      	? Success(x)
    		: Failure(['b is larger or equal to a']);
    
    
    // This doesn't work because it calls all validations on
    // every item
    /*
    const validateItem = (x) =>
      Success().concat(validateIsObject(x))
               .concat(validateHasRightKeys(x))
               .concat(validateHasRightValues(x))
               .map(_ => x);
    */
    
    // General validate until failure function:
    const validateUntilFailure = (rules) => (x) => rules.reduce(
      (result, rule) => Success.hasInstance(result) 
        ? result.concat(rule(x)) 
        : result,
      Success()
    );
    
    // Let's try it out!
    const testCases = [
      null,
      { a: 1 },
      { b: 2 },
      { a: 1, b: 2 },
      { a: 2, b: 1 }
    ];
    
    const fullValidation = validateUntilFailure([
    	validateIsObject, 
      validateHasRightKeys,
      validateHasRightValues
    ]);
    
    
    
    console.log(
      testCases
        .map(x => [x, fullValidation(x)])
        .map(stringifyResult)
        .join("\n")
    );
    
    function stringifyResult([input, output]) {
      return `input: ${JSON.stringify(input)}, ${Success.hasInstance(output) ? "success:" : "error:"} ${JSON.stringify(output.value)}`;
    }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-17
      • 2012-12-12
      • 2011-05-12
      • 1970-01-01
      相关资源
      最近更新 更多