Контекст валидации

Контекст валидации в Joi представляет собой набор данных и параметров, которые сопровождают процесс проверки значений и влияют на поведение схем, правил, сообщений об ошибках и динамических условий. Он формируется в момент вызова validate и распространяется на все уровни схемы, включая вложенные структуры, кастомные валидаторы и вычисляемые правила.


При выполнении валидации Joi оперирует не только проверяемым значением и схемой, но и дополнительными данными, которые можно рассматривать как «окружение» проверки. Это окружение включает:

  • входное значение;
  • текущую схему;
  • параметры валидации;
  • ссылки (references);
  • пользовательский контекст;
  • внутреннее состояние обхода структуры.

Контекст формируется на уровне вызова:

schema.validate(value, options)

или

Joi.validate(value, schema, options)

Именно объект options становится основным источником управляющего контекста.


Параметры валидации как часть контекста

Наиболее важные элементы контекста задаются через параметры:

  • abortEarly — определяет, прерывается ли валидация при первой ошибке;
  • convert — управляет приведением типов;
  • allowUnknown — влияет на обработку неизвестных ключей;
  • presence — задаёт обязательность значений по умолчанию;
  • stripUnknown — удаляет лишние поля;
  • context — пользовательский набор данных для использования в правилах и сообщениях.

Последний параметр имеет особое значение, так как именно он расширяет контекст произвольными данными.


Пользовательский контекст и его назначение

Пользовательский контекст позволяет передавать внешние данные в процесс валидации. Эти данные не участвуют напрямую в проверке значения, но могут использоваться:

  • в динамических условиях .when();
  • в кастомных правилах .custom();
  • в генерации сообщений об ошибках;
  • в шаблонах описаний и меток.

Пример передачи контекста:

schema.validate(data, {
  context: {
    role: 'admin',
    minAge: 18
  }
})

Использование контекста в условиях .when()

Одним из ключевых механизмов, использующих контекст, являются условные правила.

const schema = Joi.object({
  age: Joi.number().when(Joi.ref('$minAge'), {
    is: Joi.number().min(18),
    then: Joi.required(),
    otherwise: Joi.optional()
  })
});

Здесь $minAge — это ссылка на значение из контекста. Joi подставляет его из options.context.

Контекстные переменные обозначаются через $, что делает их доступными в любом месте схемы.


Контекст и ссылки (references)

Система ссылок Joi (Joi.ref) тесно связана с контекстом. Ссылка может указывать:

  • на другие поля объекта;
  • на значения из контекста;
  • на вычисляемые выражения.

Пример использования:

const schema = Joi.object({
  password: Joi.string(),
  confirm: Joi.any().valid(Joi.ref('password'))
});

Здесь контекстом выступает сам объект валидации, внутри которого происходит разрешение ссылок.

Если используется $-синтаксис:

Joi.ref('$role')

значение берётся уже из внешнего контекста, а не из проверяемого объекта.


Контекст в кастомных правилах

При создании собственных валидаторов через .custom() контекст доступен через параметры функции.

Joi.string().custom((value, helpers) => {
  const role = helpers.prefs.context.role;

  if (role === 'admin') {
    return value;
  }

  if (value === 'restricted') {
    return helpers.error('string.invalid');
  }

  return value;
});

Здесь helpers.prefs.context предоставляет доступ к внешнему контексту, переданному при валидации.


Контекст сообщений об ошибках

Контекст активно используется при формировании сообщений об ошибках. Joi поддерживает шаблонные выражения, в которых можно подставлять значения из контекста.

Пример:

const schema = Joi.number().messages({
  'number.min': 'Минимальное значение: {#limit}, роль: {#role}'
});

При расширенной настройке можно передавать дополнительные данные в ошибку через контекст:

schema.validate(value, {
  context: {
    role: 'guest'
  }
});

Эти данные становятся доступны в шаблонах сообщений.


Внутренний контекст состояния обхода

Во время рекурсивной валидации объектов Joi формирует внутренний контекст обхода структуры. Он включает:

  • путь к текущему узлу (path);
  • родительский объект;
  • текущий уровень вложенности;
  • состояние преобразования значения.

Этот контекст используется системой для:

  • генерации точных путей ошибок;
  • корректной обработки вложенных схем;
  • управления наследованием правил.

Контекст и метки (label)

Метаданные схемы также являются частью контекстного слоя. Метка (label) используется для:

  • формирования читаемых сообщений;
  • идентификации поля в ошибках;
  • подстановки в шаблоны.
Joi.string().label('Имя пользователя')

При ошибке эта информация попадает в контекст сообщения и может быть использована в формате {#label}.


Наследование контекста

Контекст в Joi наследуется сверху вниз:

  • глобальные опции validate;
  • контекст схемы;
  • вложенные схемы;
  • локальные правила.

При этом внутренние уровни могут расширять или уточнять контекст, но не всегда способны его переопределить полностью.

Особенно это важно для $-переменных, которые вычисляются на этапе разрешения ссылок и могут зависеть от уровня вложенности.


Контекст и расширение типов

При использовании Joi.extend() можно добавлять новые типы, которые получают доступ к контексту в своих внутренних методах валидации.

Joi.extend((joi) => ({
  type: 'positive',
  base: joi.number(),
  validate(value, helpers) {
    const mode = helpers.prefs.context.mode;

    if (mode === 'strict' && value <= 0) {
      return { value, errors: helpers.error('number.positive') };
    }

    return { value };
  }
}));

Контекст здесь становится частью механизма определения поведения нового типа.


Особенности работы с контекстом

Контекст Joi имеет несколько характерных свойств:

  • он динамический и формируется на момент вызова;
  • может включать произвольные данные пользователя;
  • доступен на всех уровнях схемы;
  • участвует в разрешении ссылок и условий;
  • влияет на формирование ошибок, но не изменяет исходное значение напрямую.

Эти свойства делают его ключевым механизмом для построения адаптивных схем валидации, зависящих от внешних факторов и бизнес-логики.