Email и URL валидация

В Superstruct любая валидация строится вокруг композиции структур. Для строковых данных используется базовый примитив:

import { string } from "superstruct";

const Username = string();

Такое определение само по себе не накладывает ограничений, кроме типа. Для email и URL этого недостаточно, поэтому используются дополнительные ограничения: либо через встроенные структуры, либо через композицию с pattern и refine.


Проверка email-адресов

Email — один из наиболее проблемных типов данных с точки зрения строгой валидации. Полная спецификация RFC 5322 слишком сложна для практического применения, поэтому в реальных проектах используется упрощённый, но достаточный для большинства случаев подход.

Использование встроенной структуры email

В некоторых версиях Superstruct доступны готовые валидаторы:

import { email } from "superstruct";

const Email = email();

Такая структура проверяет строку на соответствие типичному формату адреса электронной почты.

Использование:

import { assert } from "superstruct";

assert("user@example.com", Email); // проходит
assert("invalid-email", Email); // выбросит ошибку

Реализация через регулярное выражение

Если требуется более строгий контроль или кастомизация, используется pattern:

import { string, pattern } from "superstruct";

const Email = pattern(
  string(),
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/
);

Этот вариант проверяет базовую структуру:

  • наличие символа @
  • отсутствие пробелов
  • наличие доменной части
  • наличие точки в домене

Важно понимать, что даже такое выражение не охватывает всех допустимых email-форматов, но в большинстве веб-приложений этого достаточно.


Расширенная проверка через refine

Когда требуется более сложная логика, используется refine:

import { string, refine } from "superstruct";

const Email = refine(string(), "Email", (value) => {
  const basic = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
  const noDoubleDots = !value.includes("..");
  return basic && noDoubleDots;
});

Здесь добавлено дополнительное правило: запрет на последовательные точки.


Нормализация email

Валидация часто сопровождается нормализацией. Например, приведение к нижнему регистру:

const normalizeEmail = (value) => value.trim().toLowerCase();

const Email = refine(string(), "Email", (value) => {
  const normalized = normalizeEmail(value);
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized);
});

Проверка URL

URL-адреса имеют более сложную структуру, чем email, поскольку включают протокол, домен, путь, параметры и якоря.

Использование встроенной структуры url

В Superstruct может быть доступна готовая структура:

import { url } from "superstruct";

const Website = url();

Она проверяет корректность URL в стандартных схемах (http, https и др.).


Валидация через конструктор URL

Наиболее надёжный способ — использование встроенного объекта URL в Jav * aScript:

import { string, refine } from "superstruct";

const Website = refine(string(), "URL", (value) => {
  try {
    new URL(value);
    return true;
  } catch {
    return false;
  }
});

Преимущество этого подхода:

  • поддержка всех валидных URL по стандарту WHATWG
  • автоматическая проверка структуры
  • обработка сложных случаев (порт, query string, hash)

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

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

const Website = refine(string(), "URL", (value) => {
  try {
    const url = new URL(value);
    return ["http:", "https:"].includes(url.protocol);
  } catch {
    return false;
  }
});

Проверка доменной зоны

Иногда требуется контроль домена:

const Website = refine(string(), "URL", (value) => {
  try {
    const url = new URL(value);
    return url.hostname.includes(".");
  } catch {
    return false;
  }
});

Более строгий вариант может проверять конкретные TLD:

const allowedTLD = ["com", "org", "net"];

const Website = refine(string(), "URL", (value) => {
  try {
    const url = new URL(value);
    const tld = url.hostname.split(".").pop();
    return allowedTLD.includes(tld);
  } catch {
    return false;
  }
});

Комбинирование email и URL в структурах

В реальных схемах email и URL часто используются вместе, например в профиле пользователя:

import { object, string, email, url } from "superstruct";

const User = object({
  name: string(),
  email: email(),
  website: url(),
});

Такой подход позволяет централизованно описывать структуру данных и повторно использовать валидаторы.


Обработка ошибок валидации

Superstruct возвращает структурированные ошибки, которые можно использовать для диагностики:

import { validate } from "superstruct";

const [error, value] = validate("invalid-email", email());

if (error) {
  console.log(error.failures());
}

Каждая ошибка содержит:

  • путь до поля
  • причину нарушения
  • ожидаемый тип

Использование с пользовательскими сообщениями

Для улучшения UX часто добавляют преобразование ошибок:

const formatErrors = (error) => {
  return error.failures().map(f => ({
    field: f.path.join("."),
    message: "Некорректный формат значения",
  }));
};

Сочетание с преобразованием данных

Superstruct позволяет сочетать валидацию и преобразование:

import { coerce, string, email } from "superstruct";

const Email = coerce(email(), string(), (value) =>
  value.trim().toLowerCase()
);

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


Защита от некорректных пользовательских данных

При работе с email и URL важно учитывать типичные ошибки:

  • пробелы в начале и конце строки
  • отсутствие схемы в URL
  • использование нестандартных символов
  • двойные доменные точки
  • попытки SQL/JS-инъекций через строковые поля

Superstruct не заменяет безопасность приложения, но помогает отсеивать некорректные данные на входе.


Практическая композиция сложных схем

Пример структуры формы регистрации:

import { object, string, email, url, optional } from "superstruct";

const Registration = object({
  username: string(),
  email: email(),
  website: optional(url()),
  referral: optional(string()),
});

Здесь:

  • email обязателен
  • website опционален
  • остальные поля строковые

Поведение при частичной валидности данных

При валидации массивов или объектов Superstruct проверяет каждое поле независимо:

const data = {
  email: "bad-email",
  website: "https://example.com",
};

Результат ошибки будет содержать только проблемное поле email, не блокируя анализ остальных данных.


Стратегии построения устойчивой валидации

При проектировании схем рекомендуется:

  • избегать чрезмерно сложных регулярных выражений для email
  • использовать URL для проверки ссылок вместо regex
  • нормализовать входные данные до валидации
  • отделять проверку структуры от бизнес-логики
  • использовать refine только для специфичных правил

Такой подход делает систему предсказуемой и поддерживаемой даже при росте сложности проекта.