Опирается на правила:
R-VLD-STD-1…R-VLD-STD-5иR-VLD-STD-X1…R-VLD-STD-X3из Validation Style Guide → раздел 2. Стандартные constraints.
Важно знать
validator/v10читает тегиvalidate:"..."на struct-полях; нет тегов — нет валидации.requiredна non-pointer типе проверяет zero-value: дляintноль (0) тоже провалитrequired. Для числа-не-ноль используйgt=0.- Pointer-тип (
*string,*int) +omitempty= «поле необязательно; если передано — проверить остальные теги».- Форматы
e164,url,uuid4— встроены; не пиши собственный regexp для них.- Деньги —
int64в копейках (илиshopspring/decimal), неfloat64; числовой constraintgt=0, а неrequired.min=Nдля строки — длина в символах (UTF-8 rune count черезutf8.RuneCountInString); для числа — числовое значение.
validator/v10 — декларативная система тегов. Пара правил про zero-value и pointer-типы делает поведение предсказуемым: знаешь тип поля — знаешь, какой тег нужен.
Обязательные и опциональные поля
R-VLD-STD-1: граница между «поле обязательно» и «поле необязательно» строится через сочетание типа и тега.
type UpdateProductRequest struct {
Name string `json:"name" validate:"required,min=1,max=200"` // обязательно
Description *string `json:"description"` // опционально — nil = не задан
Note *string `json:"note" validate:"omitempty,max=500"` // необязательно, но если есть — не более 500
}
Логика:
string+validate:"required"— не-пустая строка (пустая""провалитrequired).*stringбезvalidate— допускается null/отсутствие в JSON.*string+validate:"omitempty,max=500"— если поле передано и не nil, проверитьmax=500; если nil — пропустить.
Размеры
R-VLD-STD-2: строки — min=N,max=M; числа — min=N,max=M (или gt=0, gte=0, lte=M):
type CreateProductRequest struct {
Name string `json:"name" validate:"required,min=1,max=200"`
PriceCop int64 `json:"price" validate:"gt=0,lte=100000000"` // в копейках, до 1 000 000 руб
Stock int `json:"stock" validate:"min=0,max=999999"`
SKU string `json:"sku" validate:"required,min=4,max=20"`
}
Для int64 с min=N: validator/v10 применяет числовое сравнение, не длину строки — тип имеет значение.
Форматы
R-VLD-STD-3: использовать встроенные теги, не самописный regexp для известных форматов.
type CustomerRequest struct {
Email string `json:"email" validate:"required,email"`
Phone string `json:"phone" validate:"required,e164"` // E.164: +79001234567
Website *string `json:"website" validate:"omitempty,url"`
ExternalID string `json:"external_id" validate:"required,uuid4"`
SberAccount *string `json:"sber_account" validate:"omitempty,min=20,max=20"` // расчётный счёт
}
Встроенные теги формата в validator/v10:
| Тег | Что проверяет |
|---|---|
email | адрес электронной почты |
e164 | международный формат телефона (+7...) |
url | полный URL с схемой |
uri | URI (без обязательной схемы) |
uuid4 | UUID v4 в canonical форме |
uuid | UUID любой версии |
hostname_port | host:port |
ip | IPv4 или IPv6 адрес |
alpha | только буквы |
alphanum | буквы и цифры |
numeric | только цифры |
Время
R-VLD-STD-4: time.Time с тегом required обязывает передать корректное значение. Для future/past — кастомный тег (см. Custom constraints):
type CreateBookingRequest struct {
StartsAt time.Time `json:"starts_at" validate:"required,future"` // future — custom
EndsAt time.Time `json:"ends_at" validate:"required"`
Duration int `json:"duration" validate:"required,min=30,max=480"` // минуты
}
time.Time — value-тип; zero-value (time.Time{}) провалит required. При декодировании JSON используй стандартный encoding/json — он обрабатывает RFC 3339. Если нужен кастомный формат, добавь UnmarshalJSON на типе или используй time.Time через json.Decoder с Layout.
Деньги
R-VLD-STD-5: деньги хранятся и передаются в минорных единицах (int64 в копейках) или через github.com/shopspring/decimal. Никогда float64 — потери точности при операциях.
type PaymentRequest struct {
OrderID string `json:"order_id" validate:"required,uuid4"`
AmountCop int64 `json:"amount_cop" validate:"gt=0"` // копейки: 10000 = 100 руб
Currency string `json:"currency" validate:"required,oneof=RUB USD EUR"`
}
gt=0 вместо required для числового поля-не-ноль: required на int64 провалится на значении 0, что семантически неверно если ноль допустим в некоторых контекстах. Для «обязательно и больше нуля» — validate:"gt=0".
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
validate:"required" на int/int64 при допустимости нуля | R-VLD-STD-X1 | validate:"gt=0" для «больше нуля»; min=0 для неотрицательного |
validate:"regexp=^[a-zA-Z0-9._%+\\-]+@..." вместо email | R-VLD-STD-X2 | validate:"email" — встроенный тег |
validate:"my_format" как один тег для нескольких стандартных правил | R-VLD-STD-X3 | Явный набор тегов через запятую: required,min=1,max=200,email |
float64 для суммы в рублях | R-VLD-STD-5 | int64 в копейках или shopspring/decimal |
validate:"required" на time.Time без обработки zero-value при парсинге | R-VLD-STD-4 | Убедиться, что JSON-парсер корректно декодирует ISO 8601 в time.Time |
Куда дальше
- Validation → раздел 2. Стандартные constraints — нормативные формулировки
R-VLD-STD-*. - Custom constraints — когда встроенных тегов недостаточно.
- Где валидировать — как применяются теги в
httpreq.Decode. - Cross-field validation — правила между несколькими полями.