【问题标题】:How to access global object property from relative property key using Joi expression如何使用 Joi 表达式从相对属性键访问全局对象属性
【发布时间】:2021-08-30 00:03:32
【问题描述】:

我正在尝试根据在validate 传递的全局上下文和正在验证的对象的现有属性来设置属性的默认值。我似乎无法将对象属性的值用作全局上下文的键。

const context = {
    letters: {
        a: 1,
        b: 2,
        c: 3,
    },
};

const s = Joi.object().keys({
    letter: Joi.string().valid(...['a', 'b', 'c']),
    num: Joi.number().default(Joi.expression('{$letters}.{letter}'))
})

const test = {
    letter: 'a',
}

console.log(s.validate(test, { context }));

上面的简单例子。我有一个名为letters 的对象,我将其放入上下文中。该模式将查找letter,然后尝试使用全局字母作为要从中提取的对象并将传入的字母作为键为num 设置默认值。我能得到的最接近的是{ value: { letter: 'a', num: '[object Object].a' } }

【问题讨论】:

  • 你不能使用来自 javascript 的模板字符串吗? `${context.letters.a}`?
  • @testing_22 不幸的是,不是这样。如果 context 是在创建 Joi 模式的范围内创建的,它有点可以工作,但是 context 无法在其他地方生成并在验证期间传入,这不是 Joi 应该的方式工作。好主意,而且,在某些情况下,它会起作用,但不是全部。
  • @Individual11 请检查我发布的答案是否满足您的要求。

标签: javascript joi


【解决方案1】:

以下一些可行的示例

Joi.expression(`{$letters[.a]}`)
Joi.expression(`{{$letters.b}}`)
Joi.expression(`{{$letters[.c]}}`)

但这里的问题是我们不能使用像 letter 这样的变量来代替 a 或 b 或 c。

但我们可以通过自定义功能实现所需的功能

// https://stackoverflow.com/a/6491621/14475852
Object.byString = function(o, s) {
  s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
  s = s.replace(/^\./, ''); // strip a leading dot
  var a = s.split('.');
  for (var i = 0, n = a.length; i < n; ++i) {
    var k = a[i];
    if (k in o) {
      o = o[k];
    } else {
      return;
    }
  }
  return o;
};

function customEvaluator(expression) {
  return function(parent, helpers) {
    let global = expression[0] == '$';
    let exp = '';
    for (var i = global ? 1 : 0, n = expression.length; i < n; ++i) {
      if (expression[i] == '#') {
        let part = expression.slice(i + 1);
        let end = -1;
        [' ', '.', '[', ']', '{', '}'].forEach(i => {
          let tmp = part.indexOf(i);
          if (end == -1 && tmp != -1) {
            end = tmp;
          }
        });

        end = end == -1 ? part.length : end;
        exp += parent[part.slice(0, end)];
        i += end;
      } else {
        exp += expression[i];
      }
    }

    return Object.byString(global ? helpers.prefs.context : parent, exp);
  };
}

const Joi = joi; // exported as joi in the browser bundle

const s = Joi.object().keys({
    letter: Joi.string().valid(...["a", "b", "c"]),
    num: Joi
        .number()
        .default(customEvaluator('$letters.#letter')),
});

const context = {
    letters: {
        a: 1,
        b: 2,
        c: 3,
    },
};

console.log(s.validate({ letter: "a" }, { context }));
console.log(s.validate({ letter: "b" }, { context }));
console.log(s.validate({ letter: "a", num: 4 }, { context }));
&lt;script src="https://cdn.jsdelivr.net/npm/joi@17.4.2/dist/joi-browser.min.js"&gt;&lt;/script&gt;

【讨论】:

    【解决方案2】:

    default 方法可以接受

    • 一个使用签名function(parent, helpers)返回默认值的函数,其中:
      • parent - 包含正在验证的值的对象的克隆。请注意,由于指定 parent 参数会执行克隆,因此如果您不使用格式参数,请不要声明它们。
      • helpers - 与any.custom() 中描述的相同。

    helpers 包含一个属性 prefs,其中包含当前首选项,包括一个 context 属性与当前上下文。

    您可以使用它来根据上下文和其他属性提供自定义默认值:

    const Joi = joi; // exported as joi in the browser bundle
    
    const context = {
        letters: {
            a: 1,
            b: 2,
            c: 3,
        },
    };
    
    const s = Joi.object().keys({
        letter: Joi.string().valid(...["a", "b", "c"]),
        num: Joi
            .number()
            .default(({ letter }, { prefs: { context } }) => context.letters[letter]),
    });
    
    const test = {
        letter: "a",
    };
    
    console.log(s.validate(test, { context }));
    console.log(s.validate({ letter: "b" }, { context }));
    console.log(s.validate({ letter: "a", num: 4 }, { context }));
    &lt;script src="https://cdn.jsdelivr.net/npm/joi@17.4.2/dist/joi-browser.min.js"&gt;&lt;/script&gt;

    【讨论】:

    • 代码运行肯定,这绝对是我以前没有考虑过的,所以谢谢。它并没有完全回答如何使用expression。我想使用expression 的原因是我可以在外部存储表达式本身。这样,我可以将表达式与代码分开更改,并且它将按预期继续运行。这个答案需要我更改实际代码才能进行更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-05
    相关资源
    最近更新 更多