Декоратор @IsByteLength в class-validator предназначен
для проверки длины строки в байтах, а не в символах. Это принципиально
важно в контексте UTF-8, где один символ может занимать от 1 до 4 байт,
что делает стандартные проверки длины недостаточными для задач,
связанных с хранением, сетевыми протоколами или ограничениями баз
данных.
В отличие от @Length, который измеряет количество
символов, @IsByteLength вычисляет фактический размер строки
в памяти после UTF-8 кодирования.
Строка преобразуется в последовательность байтов, и уже по этому представлению проверяются границы:
Таким образом, визуально короткая строка может значительно превышать допустимый лимит по байтам.
import { IsByteLength } from 'class-validator';
class CreateUserDto {
@IsByteLength(10, 50)
username: string;
}
Здесь поле username должно занимать от 10 до 50 байт в
UTF-8 представлении.
@IsByteLength принимает следующие аргументы:
@IsByteLength(5, 20, {
message: 'Длина строки должна быть от 5 до 20 байт',
})
field: string;
Ключевое различие заключается в единице измерения:
| Декоратор | Единица измерения | Пример поведения |
|---|---|---|
@IsLength |
символы | “?” = 1 символ |
@IsByteLength |
байты | “?” = 4 байта |
Это различие критично при работе с ограничениями на уровне:
VARCHAR(n) в байтах (в некоторых СУБД)UTF-8 кодирование приводит к значительным различиям между «видимой» и фактической длиной строки.
Пример:
const value = "Привет";
Хотя строка содержит 6 символов, в UTF-8 она занимает больше 6 байт из-за кириллицы.
Другой пример:
const value = "??";
Каждый emoji занимает 4 байта, следовательно строка из двух emoji уже занимает 8 байт.
Во многих схемах данных поле может быть ограничено по байтам:
@IsByteLength(0, 255)
title: string;
Это часто используется, когда:
При приёме данных через HTTP API важно гарантировать, что payload не превысит допустимый размер после сериализации:
class UpdateProfileDto {
@IsByteLength(0, 100)
displayName: string;
}
Это предотвращает ситуации, когда визуально короткое имя превращается в превышение лимита после кодирования.
Декоратор применяется только к строкам. Если значение не является строкой, валидация будет провалена (если не отключены соответствующие преобразования).
@IsByteLength(1, 10)
value: string;
Примеры невалидных значений:
numberbooleanobjectnullМожно задавать динамические сообщения:
@IsByteLength(3, 15, {
message: 'Поле должно содержать от 3 до 15 байт',
})
nickname: string;
Также доступна интерполяция контекста:
@IsByteLength(5, 20, {
message: ({ constraints }) =>
`Допустимая длина: ${constraints[0]}–${constraints[1]} байт`,
})
field: string;
В JavaScript строки хранятся в UTF-16, но проверка байтовой длины происходит после преобразования в UTF-8. Это означает, что:
Пример:
const a = "é"; // один символ
const b = "é"; // e + комбинирующий акцент
Обе строки выглядят одинаково, но их байтовая структура различается.
Частая ошибка — ожидание совпадения с количеством символов:
@IsByteLength(0, 10)
text: string;
Разработчик может предполагать, что допустимо 10 символов, но фактически:
@IsByteLength часто используется совместно:
import { IsString, IsNotEmpty, IsByteLength } from 'class-validator';
class MessageDto {
@IsString()
@IsNotEmpty()
@IsByteLength(1, 200)
content: string;
}
Такой подход разделяет ответственность:
@IsString — тип@IsNotEmpty — наличие данных@IsByteLength — технические ограничения храненияПри использовании class-transformer возможны ситуации,
когда входные данные приводятся к строке автоматически. Это может
изменить результат проверки:
@IsByteLength(2, 10)
value: string;
Если вход:
{ "value": 12345 }
После преобразования в строку "12345" проверка будет
выполнена уже по строковому представлению.
В системах, где критична экономия памяти или фиксированные структуры данных, байтовая валидация становится обязательной:
В таких случаях именно байтовая длина, а не количество символов, определяет корректность данных.