В библиотеке Joi механизм исключения значений реализуется через методы, позволяющие явно запретить определённые входные данные на уровне схемы. Это особенно важно при построении строгих API-контрактов, где помимо описания допустимых значений требуется также формализовать недопустимые случаи.
Метод invalid() задаёт значения, которые считаются
недопустимыми для конкретного поля. При совпадении входного значения с
любым из перечисленных, валидация завершается ошибкой.
const Joi = require('joi');
const schema = Joi.string().invalid('admin', 'root', 'superuser');
schema.validate('admin');
// → ошибка валидации
schema.validate('user');
// → валидно
Семантически invalid() расширяет ограничение схемы в
отрицательную сторону: вместо перечисления разрешённых значений задаётся
список запрещённых.
Запрещённые значения могут применяться не только к строкам, но и ко всем поддерживаемым типам:
const schema = Joi.number().invalid(0, -1, 999);
schema.validate(0); // ошибка
schema.validate(10); // ок
const schema = Joi.boolean().invalid(false);
schema.validate(false); // ошибка
schema.validate(true); // ок
Сравнение выполняется строго, без приведения типов:
const schema = Joi.number().invalid('5');
schema.validate(5);
// ошибка, так как '5' (строка) !== 5 (число)
Это поведение критично при работе с входными данными API, где типизация играет роль дополнительного слоя защиты.
invalid() логически противоположен valid().
При одновременном использовании действует принцип приоритета запрета:
если значение попадает в список запрещённых, оно будет отклонено даже
при наличии в списке допустимых.
const schema = Joi.number()
.valid(1, 2, 3, 4, 5)
.invalid(3);
schema.validate(3); // ошибка
schema.validate(2); // ок
Такой подход позволяет переопределять ранее заданные правила без переписывания всей схемы.
Метод допускает цепочечное использование, при котором списки объединяются:
const schema = Joi.string()
.invalid('a')
.invalid('b', 'c');
schema.validate('b'); // ошибка
Внутри библиотеки все значения агрегируются в единый набор запрещённых значений.
При нарушении ограничения invalid() возвращается
стандартный объект ошибки Joi:
const result = schema.validate('admin');
console.log(result.error.details);
Типичная структура содержит:
type: идентификатор ошибки (например,
any.invalid)message: человекочитаемое описаниеcontext.value: значение, вызвавшее ошибкуПример:
any.invalid: "value" contains an invalid value
Логика invalid() декларативна и выполняется до
пользовательских функций .custom():
const schema = Joi.string()
.invalid('test')
.custom((value, helpers) => {
return value.toUpperCase();
});
Если значение равно 'test', выполнение custom-функции не
происходит, поскольку валидация завершается на этапе проверки
invalid().
Это важно для производительности и предсказуемости цепочки валидации.
Метод disallow() исторически использовался как синоним
invalid(), однако в современных версиях Joi он считается
устаревшим (deprecated).
const schema = Joi.string().disallow('admin');
Фактически он полностью эквивалентен:
const schema = Joi.string().invalid('admin');
Основная причина отказа от disallow() заключается в
унификации API:
invalid() лучше отражает смысл операции (явный
запрет)Использование disallow() в новых проектах считается
нежелательным, хотя во многих кодовых базах он сохраняется ради обратной
совместимости.
Функционально различий нет, но есть различия в поддержке и семантике:
| Метод | Статус | Рекомендуется | Назначение |
|---|---|---|---|
| invalid() | актуальный | да | явное запрещение |
| disallow() | устаревший | нет | исторический синоним |
При работе со сложными структурами важно учитывать, что
invalid() сравнивает значения по ссылке, а не по глубокой
эквивалентности:
const schema = Joi.object({
role: Joi.string().invalid('admin')
});
schema.validate({ role: 'admin' }); // ошибка
Для объектов:
const forbidden = { type: 'admin' };
const schema = Joi.object().invalid(forbidden);
schema.validate(forbidden); // ошибка (та же ссылка)
schema.validate({ type: 'admin' }); // может быть валидно
Таким образом, для структурных сравнений требуется отдельная логика
через .custom().
invalid() не блокирует undefined и
null, если они не указаны явно:
const schema = Joi.string().invalid('admin');
schema.validate(null); // валидно
schema.validate(undefined); // валидно
Чтобы запретить такие значения, используются дополнительные методы:
const schema = Joi.string()
.invalid('admin')
.required();
или:
Joi.string().invalid('admin', null, undefined);
const username = Joi.string().invalid(
'admin',
'root',
'system'
);
const orderStatus = Joi.string().invalid('deleted');
const role = Joi.string().invalid('superadmin');
При использовании alternatives() поведение сохраняется
на уровне каждого варианта:
const schema = Joi.alternatives().try(
Joi.string().invalid('test'),
Joi.number()
);
schema.validate('test'); // ошибка
Запрет распространяется локально внутри каждой ветки схемы.
Механизм invalid() в Joi представляет собой
декларативный способ задания запрещённых значений, работающий на раннем
этапе валидации и имеющий приоритет над другими правилами схемы.
Устаревший disallow() сохраняется только для обратной
совместимости и не должен использоваться в новых схемах.