Есть ли способ объединить схемы Джой?
Есть ли способ объединить две схемы joi в одну схему?
Схема 1
{
alpha: Joi.number().required(),
beta: Joi.string().required(),
chalie: Joi.object({
xray: Joi.number().required(),
}).required()
}
Схема 1
{
delta: Joi.string().required(),
echo: Joi.number().required(),
charlie: Joi.object({
zulu: Joi.string().required(),
}).required()
}
Объединенная схема:
{
alpha: Joi.number().required(),
beta: Joi.string().required(),
chalie: Joi.object({
xray: Joi.number().required(),
zulu: Joi.string().required(),
}).required()
delta: Joi.string().required(),
echo: Joi.number().required(),
}
Без вложенных объектов это легко сделать с Object.assign
, но даже глубокое слияние объектов не будет работать с вложенными объектами, потому что вложенный объект является вызовом функции.
8 ответов
Мне было интересно то же самое, так как я хотел объединить две разные схемы и нашел это: https://github.com/hapijs/joi/blob/v9.0.4/API.md
const a = Joi.string().valid('a');
const b = Joi.string().valid('b');
const ab = a.concat(b);
Надеюсь, это поможет тебе
Вы пробовали Joi.append?
https://github.com/hapijs/joi/blob/v13.5.2/API.md#objectkeysschema
// Validate key a
const base = Joi.object().keys({
a: Joi.number()
});
// Validate keys a, b.
const extended = base.append({
b: Joi.string()
});
Использование простых объектов javascript для меня было невозможным. Я пытался использовать .keys
метод расширения, но он перезаписывает существующие ключи (для Чарли в этом случае).
Решение, на котором я остановился, использовало .reach
: Пример:
const Joi = require('joi');
const originalSchema = Joi.object({
a: {
deep: {
b: Joi.string()
}
},
c: Joi.string()
});
const extendedSchema = Joi.object({
a: {
deep: Joi
.reach(originalSchema, 'a.deep')
.keys({ anotherB: Joi.string() })
},
c: Joi.reach(originalSchema, 'c')
});
// No errors
extendedSchema.validate({ a: { deep: { b: 'hi', anotherB: 'hi' } }, c: 'wow' })
Joi.object()
и оператор спреда
...
сделал трюк для меня.(Джой версия 17)
import * as Joi from 'joi'
const childSchema: {
PORT: Joi.number(),
}
const parentSchema = Joi.object({
NODE_ENV: Joi.string(),
APP_NAME: Joi.string(),
...childSchema,
})
Мне здесь не понравился ни один из ответов, поэтому я нашел другой метод. Я создал класс, чтобы установить одно правило для элемента, например адрес электронной почты или пароль, с одной точкой происхождения для требований, а не с несколькими схемами в разных файлах. или даже несколько полуизбыточных схем в одном файле / классе.
Стоит отметить, что.append не работает, если первое правило пусто. Здесь на помощь приходит.concat.
Сначала я создал класс с парой правил для одного элемента
//an email address
static emailAddress = Joi.object({
emailAddress: Joi.string()
.email({ tlds: { allow: false } })
.required()
.label("Email Address"),
});
static passwordRegex = /^(?=.*[A-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])\S{8,}$/;
static passwordError =
"Password must be at least 8 characters long, and have at least one uppercase letter, one lowercase letter, one number, and one special character.";
//a password
static password = Joi.object({
password: Joi.string()
.min(8)
.regex(this.passwordRegex)
.message(this.passwordError)
.label("Password"),
});
Затем я создал пару правил для конкретных объектов, которые мне нужно было проверить.
static registerUserSchema() {
let schema = Joi.object()
.concat(this.yourNameSchema)
.concat(this.emailAddress)
.concat(this.password)
.concat(this.confirmPassword);
return schema;
}
Мне потребовалась целая вечность, чтобы понять, но это работает безупречно.
Вот расширение ответа @szanata, это рабочий пример слияния нескольких схем, используемых для проверки тела запроса. Я создал это как промежуточное программное обеспечение для маршрутов, и иногда у меня есть до 3 схем, проверяющих тело запроса. Вы можете передать одну схему или массив схем.
const validateRequest = (schema) => {
return (req, res, next) => {
if(Array.isArray(schema)){
let schemas = schema;
schema = schemas[0]
schemas.forEach((s, idx) => {
if (idx > 0) schema = schema.concat(s);
});
}
let data = { ...req.body, ...req.query, ...req.params };
const { error } = schema.validate(data, options);
if (error) res.status(422).json({msg:`${error.details.map((x) => x.message).join(", ")}`})
else next();
}
}
Пример использования в качестве промежуточного ПО для маршрута:
const { query, mongoDbId, test } = require("../utils/validation-schema");
const router = express.Router();
router.post("/test", protect, validateInput([mongoDbId, test, query]),
(req, res) => {
res.json({ msg: "OK" });
});
Вывод console.log(schema._ids) после concat.
{
_byId: Map {},
_byKey: Map {
'_id' => { schema: [Object], id: '_id' },
'databaseType' => { schema: [Object], id: 'databaseType' },
'host' => { schema: [Object], id: 'host' },
'database' => { schema: [Object], id: 'database' },
'user' => { schema: [Object], id: 'user' },
'password' => { schema: [Object], id: 'password' },
'datasource' => { schema: [Object], id: 'datasource' },
'sql' => { schema: [Object], id: 'sql' },
'modifier' => { schema: [Object], id: 'modifier' },
'sqlType' => { schema: [Object], id: 'sqlType' },
'format' => { schema: [Object], id: 'format' },
'timeout' => { schema: [Object], id: 'timeout' }
},
_schemaChain: false
}
Хотя вы можете использовать Javascript Object.assign()
Я думаю, что вы ищете Джой .keys()
функция.
В вашем коде я бы сделал:
const schema1 = Joi.object({
alpha: Joi.number().required(),
beta: Joi.string().required(),
charlie: Joi.object({
xray: Joi.number().required(),
}).required()
});
const schema2 = Joi.object({
delta: Joi.string().required(),
echo: Joi.number().required(),
charlie: Joi.object({
zulu: Joi.string().required(),
}).required()
});
const mergedSchema = schema1.keys(schema2);
Есть также интересное примечание об использовании прямых объектов JS против обертывания их в Joi.object()
;
При использовании нотации {} вы просто определяете простой объект JS, который не является объектом схемы. Вы можете передать его методу валидации, но не можете вызвать метод объекта validate(), потому что это простой объект JS.
Кроме того, каждый раз передача объекта {} методу validate() будет выполнять дорогостоящую операцию компиляции схемы при каждой проверке.
Когда вы используете Joi.object([schema]), он компилируется в первый раз, так что вы можете передать его в метод validate() несколько раз, и никакие накладные расходы не добавляются.
Таким образом, вы можете принять предложение Ankh и использовать прямые объекты JS:
const schema1 = {
alpha: Joi.number().required(),
beta: Joi.string().required(),
charlie: Joi.object({
xray: Joi.number().required(),
}).required()
};
const schema2 ={
delta: Joi.string().required(),
echo: Joi.number().required(),
charlie: Joi.object({
zulu: Joi.string().required(),
}).required()
};
const mergedSchema = Object.assign({}, schema1, schema2);
но есть связанное снижение производительности.
https://github.com/hapijs/joi/blob/v15.0.1/API.md
object.append([schema]) Добавляет разрешенные ключи объекта, где:
schema - необязательный объект, где каждому ключу назначен объект типа joi. Если схема имеет значение null,undefined или {}, изменения не применяются. Использует object.keys([schema]) для добавления ключей.
// Validate key a const base = Joi.object().keys({ a: Joi.number() }); // Validate keys a, b. const extended = base.append({ b: Joi.string() });