Санитизация входных данных в контексте использования
class-validator в JavaScript-проектах опирается на тесную
интеграцию с механизмами преобразования объектов и строгого разделения
ответственности между проверкой данных и их приведением к безопасному и
предсказуемому виду. В реальных приложениях эти процессы почти всегда
используются совместно: валидация отвечает за соответствие структуры и
ограничений, а санитизация — за нормализацию и очистку входных значений
до выполнения бизнес-логики.
В экосистеме Node.js и TypeScript часто наблюдается смешение двух процессов, однако их логика принципиально различается:
class-validator реализует именно валидационный слой и не
предназначен для модификации входных данных. Например, декораторы
@IsString(), @IsEmail(),
@Length() не изменяют значение, а только проверяют его.
Санитизация в таких архитектурах обычно делегируется
class-transformer, который работает совместно с
class-validator, формируя связку преобразования и проверки
DTO-объектов.
Основной механизм очистки и нормализации данных реализуется через
декоратор @Transform():
import { Transform } from 'class-transformer';
import { IsString } from 'class-validator';
export class CreateUserDto {
@Transform(({ value }) => value?.trim())
@IsString()
username: string;
}
Здесь происходит классическая операция санитизации — удаление
пробелов по краям строки. Важно, что преобразование выполняется до
валидации, если включена опция transform: true при
использовании plainToInstance.
Ключевым аспектом является порядок выполнения операций:
@Transformclass-validatorПример конфигурации:
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
const dto = plainToInstance(CreateUserDto, requestBody, {
enableImplicitConversion: true,
});
const errors = await validate(dto);
Такой порядок позволяет сначала нормализовать входные данные, а затем проверить их корректность уже в очищенном виде.
В прикладных системах наиболее часто применяются следующие преобразования:
@Transform(({ value }) => typeof value === 'string' ? value.trim() : value)
name: string;
Используется для устранения случайных пробелов, влияющих на уникальность и сравнение значений.
@Transform(({ value }) => typeof value === 'string' ? value.toLowerCase() : value)
email: string;
Позволяет унифицировать данные перед проверкой уникальности или поиском.
@Transform(({ value }) => Number(value))
age: number;
Часто используется при получении данных из HTTP-запросов, где числа приходят строками.
@Transform(({ value }) =>
typeof value === 'string'
? value.replace(/[<>]/g, '')
: value
)
comment: string;
Такая операция применяется для минимальной фильтрации пользовательского ввода перед дальнейшей обработкой.
Санитизация не должна подменять валидацию. Например, следующая ошибка архитектуры приводит к скрытым проблемам:
@Transform(({ value }) => value.replace(/[^0-9]/g, ''))
@IsNumber()
phone: string;
Здесь происходит попытка превратить произвольную строку в число через удаление символов, что может маскировать некорректные данные. Более корректный подход:
@Transform(({ value }) => value)
@IsString()
@Matches(/^\+?[0-9]{10,15}$/)
phone: string;
Санитизация должна быть предсказуемой и не скрывать ошибки входных данных.
Флаг enableImplicitConversion позволяет автоматически
приводить типы:
plainToInstance(Dto, data, {
enableImplicitConversion: true,
});
Это влияет на санитизацию следующим образом:
"123" могут стать числом 123"true" может быть преобразовано в
trueОднако такое поведение требует осторожности, так как может снижать строгость проверки и приводить к неожиданным результатам в сложных DTO-структурах.
При работе с вложенными DTO санитизация должна явно распространяться на дочерние структуры:
export class AddressDto {
@Transform(({ value }) => value?.trim())
city: string;
}
export class UserDto {
@ValidateNested()
@Type(() => AddressDto)
address: AddressDto;
}
Без @Type() преобразование вложенных объектов не
выполняется, и санитизация внутри них не сработает.
Санитизация массивов требует отдельного подхода:
@Transform(({ value }) =>
Array.isArray(value)
? value.map(v => v.trim())
: []
)
tags: string[];
Часто используется нормализация списков тегов, категорий или идентификаторов.
Санитизация должна учитывать риск потери информации:
Поэтому трансформации должны быть минимальными и детерминированными.
При необходимости сложной логики санитизации используется связка кастомного декоратора и трансформации:
@Transform(({ value }) => value?.trim())
@ValidatorConstraint({ name: 'isSafeText' })
class IsSafeTextConstraint implements ValidatorConstraintInterface {
validate(text: string) {
return !/[<>]/.test(text);
}
}
Такой подход разделяет очистку (trim) и проверку безопасности (валидация).
Типичный pipeline выглядит следующим образом:
@Transformclass-validatorЭто обеспечивает предсказуемость состояния данных на каждом этапе.
Использование class-validator совместно с
class-transformer не заменяет полноценные механизмы защиты
входных данных на уровне инфраструктуры:
Санитизация на уровне DTO — это лишь первый слой нормализации данных перед дальнейшей обработкой.