Регистрозависимость в Joi проявляется прежде всего при сравнении строковых значений и проверке их соответствия заданным правилам. По умолчанию библиотека рассматривает символы в разных регистрах как различные, что влияет на поведение методов валидации, работающих со строками, перечислениями допустимых значений и регулярными выражениями.
При использовании Joi.string() и последующих ограничений
проверка выполняется строго, без приведения к единому регистру. Это
означает, что значения "Admin" и "admin"
считаются различными.
Такое поведение проявляется во всех конструкциях, где происходит сравнение строк:
valid)invalid)equal)Пример логики:
"user""User" → ошибка валидации"user" → проходит проверкуЭто важно учитывать при проектировании API, где источником данных выступают формы, внешние сервисы или пользовательский ввод.
Методы valid() и invalid() используют
строгое сравнение строк. Без дополнительных опций значения считаются
различными при любом отличии регистра.
Joi.string().valid('admin', 'user')
Допустимыми будут только точные совпадения:
"admin" → допустимо"Admin" → недопустимо"USER" → недопустимоДля устранения этой проблемы используется метод
insensitive().
Метод insensitive() изменяет поведение сравнения
строковых значений, делая его регистронезависимым.
Joi.string().valid('admin', 'user').insensitive()
Теперь допустимы варианты:
"admin""Admin""ADMIN""user""User"При этом сравнение происходит по нормализованной форме, но исходное значение не изменяется автоматически.
Аналогично работает и invalid():
Joi.string().invalid('admin').insensitive()
Любая форма написания "admin" будет считаться
недопустимой.
Валидация через регулярные выражения остаётся одним из основных источников регистрозависимого поведения.
Joi.string().pattern(/abc/)
В этом случае:
"abc" → допустимо"ABC" → недопустимо"aBc" → недопустимоЧтобы сделать проверку регистронезависимой, используется флаг
i:
Joi.string().pattern(/abc/i)
Теперь совпадения допускаются в любом регистре:
"abc""ABC""AbC"Важно учитывать, что использование регулярных выражений с флагом
i полностью перекладывает контроль над регистром на саму
регулярку, а Joi не выполняет дополнительной нормализации.
Метод insensitive() работает только с механизмами
сравнения значений, а не с произвольными преобразованиями строки.
Он применяется:
valid()invalid()equal()Он не влияет на:
pattern()email()uri()custom()Это создаёт важное различие: часть проверок может быть регистронезависимой, а часть — строго чувствительной, даже внутри одной схемы.
При валидации объектов через Joi.object() ключи также
чувствительны к регистру.
Joi.object({
user: Joi.string()
})
Объект:
{
User: "value"
}
не будет соответствовать схеме, так как ключ User
отличается от user.
Joi не выполняет автоматического приведения ключей к нижнему или верхнему регистру. Для работы с нечувствительными к регистру ключами требуется предварительная нормализация данных или использование пользовательских преобразований.
Некоторые типы в Joi могут выполнять преобразования значений, но они не связаны напрямую с регистром. Например, строка может быть преобразована из другого типа, но её регистр сохраняется.
Joi.string().required()
Значение "TEXT" останется "TEXT", если не
применяется явное преобразование:
trim() удаляет пробелы, но не меняет регистрlowercase() приводит строку к нижнему региструuppercase() приводит строку к верхнему региструЭти методы позволяют управлять регистром явно:
Joi.string().lowercase()
Теперь любое значение будет приведено к нижнему регистру перед проверкой.
На практике часто комбинируются преобразования и проверки:
Joi.string()
.lowercase()
.valid('admin', 'user')
В этом случае:
"ADMIN" → преобразуется в "admin" →
проходит проверку"User" → преобразуется в "user" →
проходит проверкуТакой подход позволяет централизованно устранить проблемы
регистрозависимости, не полагаясь на insensitive().
Механизмы выглядят схожими, но имеют принципиальное различие:
insensitive() не изменяет значение, только влияет на
сравнениеlowercase() изменяет само значение до этапа
проверкиЭто влияет на итоговое значение после валидации:
insensitive() вход остаётся в исходном регистреlowercase() результат всегда нормализованВыбор подхода зависит от того, требуется ли сохранить оригинальный ввод или привести данные к единому формату.
При использовании вложенных схем поведение регистрозависимости
сохраняется на каждом уровне отдельно. Например, внутри
alternatives() или вложенных object() правила
сравнения не наследуются автоматически, а применяются локально к каждому
узлу схемы.
Это приводит к тому, что в одной структуре могут одновременно существовать поля:
Такое поведение требует явного контроля за каждой частью схемы, особенно в API с большим количеством строковых полей.