Joi-多个`when`子句

问题描述

我有两个对同一个有效负载执行的验证:

如果hasSalary为真,则必须存在monthlySalaryannualSalary

如果hasCosts为真,则必须存在monthlyCostsannualCosts

我将其编码为:

Joi.object({
  hasSalary: Joi.boolean(),monthlySalary: Joi.number(),annualSalary: Joi.number(),hasCosts: Joi.boolean(),monthlyCosts: Joi.number(),annualCosts: Joi.number(),})
.when(
  Joi.object({ hasSalary: Joi.boolean().valid(true).required() }),{
    then: Joi.object().xor('monthlySalary','annualSalary')
  }
)
.when(
  Joi.object({ hasCosts: Joi.boolean().valid(true).required() }),{
    then: Joi.object().xor('monthlyCosts','annualCosts')
  }
);

这正确地给出了以下验证错误{ hasSalary: true }

message: '"value" must contain at least one of [monthlySalary,annualSalary]'

...以及{ hasCosts: true }

message: '"value" must contain at least one of [monthlyCosts,annualCosts]'

...但是当两个布尔值都为true并且不满足第二个when的约束时,它无法按我预期的那样工作:

{
  hasSalary: true,monthlySalary: 300,hasCosts: true,}

我希望在这里使用"value" must contain at least one of [monthlyCosts,annualCosts],但是我得到了没有错误的明确验证。

我想我了解发生了什么事-连锁when正在创建一系列后卫,而第一个匹配的后卫获胜。

那么我可以在Joi(理想的版本15)中使用什么构造来实现我想要的?

解决方法

使用最新版本的Joi 17.2.1,您不会遇到此问题(当条件正确解决时会出现多个问题)

但是对于Joi 15.1.1,您可以使用以下解决方法:

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

const one = Joi.object({
  hasSalary: Joi.boolean().valid(true),monthlySalary: Joi.number(),annualSalary: Joi.number(),}).xor('monthlySalary','annualSalary');

const two = Joi.object({
  hasSalary: Joi.boolean().valid(false),});

const three = Joi.object({
  hasCosts: Joi.boolean().valid(true),monthlyCosts: Joi.number(),annualCosts: Joi.number(),}).xor('monthlyCosts','annualCosts');

const four = Joi.object({
  hasCosts: Joi.boolean().valid(false),});

const one_three = one.concat(three);
const one_four = one.concat(four);
const two_three = two.concat(three);
const two_four = two.concat(four);

const schema = Joi.alternatives().try(
  one,two,three,four,one_three,one_four,two_three,two_four,);

运行一些测试:

// works
const data1 = {
  hasSalary: true,monthlySalary: 2000,};
console.log(schema.validate(data1).error);

// works
const data2 = {
  hasSalary: false,};
console.log(schema.validate(data2).error);

// works
const data3 = {
  hasCosts: true,monthlyCosts: 300,};
console.log(schema.validate(data3).error);

// works
const data4 = {
  hasCosts: false,};
console.log(schema.validate(data4).error);

// works
const data5 = {
  hasSalary: true,hasCosts: true,};
console.log(schema.validate(data5).error);

// works
const data6 = {
  hasSalary: false,};
console.log(schema.validate(data6).error);

// works
const data7 = {
  hasSalary: true,hasCosts: false,};
console.log(schema.validate(data7).error);

// works
const data8 = {
  hasSalary: false,};
console.log(schema.validate(data8).error);

// error
const data9 = {
  hasSalary: true
};
console.log(schema.validate(data9).error.message)

// error
const data10 = {
  hasSalary: true,annualSalary: 1000,};
console.log(schema.validate(data10).error.message)