将所有属性标记为是否需要 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());
我正在构建一个 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());