Валидация данных в Go (Golang): полное руководство по библиотекам и подходам
Валидация данных — критически важная часть любого веб-приложения, API или микросервиса. В Go (Golang) эта задача решается как стандартными средствами, так и мощными сторонними библиотеками. В этой статье мы разберём основные подходы, популярные библиотеки и приведём практические примеры кода.
Зачем нужна валидация в Go?
Go — строго типизированный язык, что уже даёт некоторую защиту от некорректных данных. Однако типы не проверяют бизнес-правила: email должен содержать «@», возраст — быть в диапазоне 0–120, пароль — иметь минимальную длину. Без валидации вы рискуете получить:
- SQL-инъекции и XSS-атаки
- Некорректные данные в БД
- Неожиданные паники и ошибки времени выполнения
Встроенные средства валидации
Простейшая валидация делается «руками» в функциях. Пример:
type User struct {
Name string
Email string
Age int
}
func ValidateUser(u User) error {
if u.Name == "" {
return errors.New("name is required")
}
if !strings.Contains(u.Email, "@") {
return errors.New("invalid email")
}
if u.Age < 0 || u.Age > 150 {
return errors.New("age must be between 0 and 150")
}
return nil
}
Этот подход прост, но для сложных структур становится громоздким и трудно поддерживаемым. Поэтому Go-сообщество создало специализированные библиотеки.
1. go-playground/validator — стандарт де-факто
Ссылка: github.com/go-playground/validator/v10
Это самая популярная библиотека валидации в Go (более 15k звёзд на GitHub). Она использует теги структур (struct tags) для декларативного описания правил.
Установка
go get github.com/go-playground/validator/v10
Пример использования
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type User struct {
Name string `validate:"required,min=2,max=50"`
Email string `validate:"required,email"`
Age int `validate:"gte=0,lte=150"`
Password string `validate:"required,min=8"`
}
func main() {
validate := validator.New()
user := User{
Name: "A",
Email: "not-email",
Age: 200,
Password: "123",
}
err := validate.Struct(user)
if err != nil {
for _, e := range err.(validator.ValidationErrors) {
fmt.Printf("Поле: %s, правило: %s, значение: %v\n", e.Field(), e.Tag(), e.Value())
}
}
}
Вывод:
Поле: Name, правило: min, значение: A
Поле: Email, правило: email, значение: not-email
Поле: Age, правило: lte, значение: 200
Поле: Password, правило: min, значение: 123
Популярные теги-правила
required— поле обязательноmin,max— для чисел и строкlen— точная длинаemail— проверка emailurl— проверка URLgte,lte— больше/меньше или равноoneof— одно из значений (oneof=admin user)
Кастомные валидаторы
validate.RegisterValidation("is-odd", func(fl validator.FieldLevel) bool {
return fl.Field().Int()%2 != 0
})
type MyStruct struct {
Number int `validate:"is-odd"`
}
2. ozzo-validation — альтернатива с цепочками
Ссылка: github.com/go-ozzo/ozzo-validation/v4
Эта библиотека предлагает более «Go-идиоматичный» подход: правила задаются через функции, а не теги. Это даёт большую гибкость и читаемость.
Пример
package main
import (
"fmt"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
)
type User struct {
Name string
Email string
Age int
Password string
}
func (u User) Validate() error {
return validation.ValidateStruct(&u,
validation.Field(&u.Name, validation.Required, validation.Length(2, 50)),
validation.Field(&u.Email, validation.Required, is.Email),