@Contains, @NotContains

Декоратор @Contains в библиотеке class-validator используется для проверки наличия подстроки внутри строкового значения свойства класса. Его основная задача — гарантировать, что значение содержит определённый фрагмент текста, без необходимости вручную писать кастомные валидаторы.

Базовое поведение

@Contains принимает строковый аргумент — подстроку, которая должна присутствовать в проверяемом значении.

При валидации происходит простая операция проверки:

  • значение приводится к строковому типу (если возможно)
  • выполняется поиск подстроки
  • при отсутствии совпадения возвращается ошибка валидации

Сигнатура

Contains(substring: string, validationOptions?: ValidationOptions)
  • substring — обязательная строка, которая должна содержаться в значении
  • validationOptions — дополнительные параметры поведения и сообщений об ошибках

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

import { Contains } from "class-validator";

class UserDto {
  @Contains("admin")
  role: string;
}

В этом случае значение role должно содержать подстроку "admin".

Допустимые значения:

  • "superadmin"
  • "admin-user"
  • "i-am-admin"

Недопустимые значения:

  • "user"
  • "administrator" (важно: не содержит точную подстроку "admin" как последовательность символов в нужном виде, если проверка строгая к подстроке)
  • "root"

Поведение с регистрами символов

@Contains чувствителен к регистру. Это означает, что:

@Contains("Admin")
role: string;

не пропустит значение:

  • "admin-panel"

но пропустит:

  • "SuperAdmin"

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


Практическое применение @Contains

Валидация форматов ролей

Часто используется в системах доступа:

class AccessDto {
  @Contains("role_")
  scope: string;
}

Такой подход гарантирует, что все значения следуют единому соглашению именования:

  • role_admin
  • role_moderator
  • role_user

Проверка структурированных строк

Некоторые системы используют строковые коды:

class TrackingDto {
  @Contains("track-")
  code: string;
}

Это позволяет ограничить ввод только значениями, соответствующими внутреннему формату:

  • track-001
  • track-abc-99

Ограничения @Contains

Несмотря на простоту, декоратор имеет ряд ограничений:

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

  2. Риск ложных совпадений Например:

    @Contains("admin")
    "notadminrole"

    формально проходит проверку, но может быть нежелательным значением.

  3. Нет поддержки сложных правил Невозможно задать условия вроде:

    • «содержит A и не содержит B»
    • «содержит A только в начале строки»

Комбинирование с другими валидаторами

@Contains часто используется совместно с другими декораторами для повышения точности проверки.

С @IsString

import { IsString, Contains } from "class-validator";

class ExampleDto {
  @IsString()
  @Contains("prefix_")
  value: string;
}

С @MinLength и @MaxLength

import { Contains, MinLength, MaxLength } from "class-validator";

class CodeDto {
  @MinLength(10)
  @MaxLength(50)
  @Contains("ID-")
  code: string;
}

Такой набор ограничивает как структуру, так и размер строки.


Поведение при пустых и некорректных значениях

  • null и undefined не проходят проверку без дополнительных декораторов (@IsOptional)
  • пустая строка "" всегда не проходит проверку, если подстрока не пуста
  • нестроковые значения приводятся к строке, что может привести к неожиданным результатам

Пример:

@Contains("123")
value: any;

Значение 123 будет преобразовано в "123" и успешно пройдет проверку.


Опции ValidationOptions

@Contains("admin", {
  message: "Значение должно содержать 'admin'"
})
role: string;

Возможности:

  • кастомное сообщение ошибки
  • группы валидации
  • условное применение через each, groups

Декоратор @NotContains

Назначение

@NotContains выполняет противоположную задачу: проверяет, что строка не содержит указанную подстроку.

Сигнатура

NotContains(substring: string, validationOptions?: ValidationOptions)
  • substring — запрещённая подстрока
  • validationOptions — параметры ошибки

Базовое использование

import { NotContains } from "class-validator";

class PasswordDto {
  @NotContains("123")
  password: string;
}

Значения:

  • "securePass!" — валидно
  • "my123pass" — невалидно
  • "123456" — невалидно

Практические сценарии применения @NotContains

Ограничение небезопасных символов

class InputDto {
  @NotContains("<script>")
  comment: string;
}

Используется как дополнительный слой защиты, но не заменяет полноценную санитизацию.


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

class FilenameDto {
  @NotContains("..")
  filename: string;
}

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

  • file.txt — допустимо
  • ../secret.txt — недопустимо

Особенности работы @NotContains

Чувствительность к регистру

Аналогично @Contains, проверка строго регистрозависимая:

@NotContains("Admin")

не блокирует "adminPanel".


Побочные эффекты приведения типов

Любое значение, приводимое к строке, может пройти или не пройти проверку неожиданным образом:

  • 123"123"
  • null"null"

Это требует аккуратного использования вместе с @IsString.


Комбинация @Contains и @NotContains

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

class FilterDto {
  @Contains("user")
  @NotContains("test")
  tag: string;
}

Такое сочетание задаёт условие:

  • обязательно наличие "user"
  • запрет на "test"

Примеры:

  • "user_active" — допустимо
  • "test_user" — недопустимо
  • "guest_user" — допустимо

Типовые ошибки при использовании

Избыточное использование для сложных правил

Попытка выразить бизнес-логику через @Contains приводит к нечитаемым ограничениям:

@Contains("A")
@Contains("B")
@NotContains("C")

Такой код сложно сопровождать и масштабировать.


Подмена регулярных выражений

@Contains не заменяет @Matches. При необходимости точного контроля структуры строки предпочтительнее использовать регулярные выражения:

@Matches(/^admin_[a-z]+$/)

Ложное чувство безопасности

@NotContains не является механизмом защиты от атак. Например:

  • HTML/JS инъекции не блокируются полностью
  • кодировка может обходить проверку

Внутренняя логика проверки

Обе функции реализованы поверх простой строковой операции:

  • indexOf(substring) !== -1 для @Contains
  • indexOf(substring) === -1 для @NotContains

Это объясняет их высокую производительность и одновременно ограниченную выразительность.


Роль в архитектуре валидации DTO

@Contains и @NotContains чаще всего применяются в DTO-слое:

  • фильтрация входных данных API
  • первичная проверка форм
  • ограничение формата строковых полей

Их задача — не полная защита, а структурное ограничение входных данных до передачи в бизнес-логику.