URL валидация

В Yup проверка URL выполняется преимущественно через встроенный метод url(), который применяется к строковым схемам. Этот метод реализует базовую валидацию по стандарту URI и опирается на достаточно строгую регулярную проверку, охватывающую распространённые сценарии использования веб-адресов.

Базовый механизм проверки строится на цепочке методов строковой схемы:

import * as yup from 'yup';

const schema = yup.object({
  website: yup.string().url()
});

Метод url() проверяет соответствие значения формату абсолютного URL, включая схему (protocol), домен и допустимые символы. Допустимыми считаются адреса вида:

Ключевая особенность стандартной проверки заключается в требовании наличия протокола. Значения без http:// или https:// считаются невалидными.

Сообщения об ошибках и кастомизация

Поведение метода url() допускает передачу собственного сообщения ошибки:

yup.string().url('Некорректный URL')

Сообщение применяется при несоответствии значения формату URL, включая случаи отсутствия домена, протокола или нарушения структуры адреса.

Обязательность и пустые значения

Валидация URL часто комбинируется с ограничением обязательности поля:

yup.string()
  .url()
  .required()

При этом required() отрабатывает раньше проверки формата. Пустая строка рассматривается как отсутствие значения, если не отключена предварительная трансформация.

Дополнительно применяется:

yup.string()
  .url()
  .nullable()

Конструкция nullable() допускает значение null, исключая его из проверки формата URL.

Нормализация входных данных

Перед проверкой URL часто выполняется предварительная обработка:

yup.string()
  .transform((value) => value?.trim())
  .url()

Подобная трансформация устраняет пробелы в начале и конце строки, которые иначе приводят к ошибкам валидации.

Ограничение протоколов

Встроенный метод url() не позволяет явно ограничивать список допустимых протоколов. Для этого используется расширение через test():

yup.string()
  .url()
  .test('protocol', 'Недопустимый протокол', (value) => {
    if (!value) return true;
    return value.startsWith('https://');
  })

Подобная проверка применяется при необходимости жёсткого контроля безопасности или политики передачи данных.

Расширенная кастомная валидация URL

При более сложных требованиях используется комбинирование test() с парсингом URL:

yup.string()
  .test('valid-url', 'Некорректный URL', (value) => {
    if (!value) return false;

    try {
      const url = new URL(value);
      return !!url.hostname;
    } catch {
      return false;
    }
  })

Использование встроенного класса URL позволяет учитывать специфику парсинга браузера и Node.js, включая обработку портов, query-параметров и hash-фрагментов.

Ограничение доменной зоны

Часто требуется контроль допустимых доменов верхнего уровня:

yup.string().test('tld', 'Недопустимый домен', (value) => {
  if (!value) return false;

  try {
    const { hostname } = new URL(value);
    return hostname.endsWith('.com') || hostname.endsWith('.org');
  } catch {
    return false;
  }
});

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

Работа с локальными адресами

Стандартная проверка url() не всегда корректно обрабатывает локальные адреса:

Для поддержки таких значений используется кастомная логика:

yup.string().test('local-or-url', (value) => {
  if (!value) return false;

  try {
    const url = new URL(value);
    return (
      url.hostname === 'localhost' ||
      /^(\d{1,3}\.){3}\d{1,3}$/.test(url.hostname)
    );
  } catch {
    return false;
  }
});

Относительные URL

Метод url() не предназначен для относительных путей. Значения вида /path/page или ../resource считаются невалидными. Для их обработки используется отдельная логика:

yup.string().test('relative-url', (value) => {
  if (!value) return true;
  return value.startsWith('/');
});

Международные домены

URL с Unicode-символами требуют нормализации через Punycode. Встроенная проверка Yup не всегда учитывает такие случаи, поэтому применяется предварительное преобразование:

yup.string().transform((value) => {
  try {
    const url = new URL(value);
    return url.toString();
  } catch {
    return value;
  }
});

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

Интеграция в сложные схемы объектов

URL-поля часто являются частью составных объектов:

const schema = yup.object({
  profile: yup.object({
    avatar: yup.string().url(),
    website: yup.string().url().nullable()
  })
});

В таких структурах ошибки валидации агрегируются по путям вложенности, что позволяет точно локализовать некорректное значение.

Асинхронная проверка URL

При необходимости проверки существования ресурса применяется асинхронный test():

yup.string().url().test('exists', async (value) => {
  if (!value) return false;

  try {
    const response = await fetch(value, { method: 'HEAD' });
    return response.ok;
  } catch {
    return false;
  }
});

Такой подход увеличивает точность, но требует учёта сетевых задержек и отказоустойчивости.

Комбинирование нескольких правил

URL-валидация редко используется изолированно. Часто комбинируются дополнительные ограничения:

yup.string()
  .trim()
  .lowercase()
  .url()
  .required()
  .max(2048)

Ограничение длины важно для предотвращения атак через чрезмерно длинные URL и соблюдения стандартов браузеров и серверов.

Особенности поведения url() в Yup

  • Требует абсолютный адрес
  • Проверяет структуру домена и протокола
  • Не поддерживает относительные пути
  • Не валидирует доступность ресурса
  • Не ограничивает доменные зоны
  • Не выполняет сетевые запросы

Эти ограничения определяют необходимость расширения стандартного поведения через test() и внешние инструменты парсинга URL.