Валидация данных в Go: библиотеки, примеры и лучшие практики

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Валидация данных в Go (Golang): инструменты, примеры и лучшие практики

Валидация данных — один из ключевых этапов разработки любого веб-приложения или API. В Go (Golang) эта задача решается особенно элегантно благодаря строгой типизации, мощным библиотекам и встроенным возможностям. В этой статье мы разберём основные подходы к проверке данных, популярные библиотеки и приведём практические примеры кода.

Зачем нужна валидация в Go?

Go компилируется в нативный код и не имеет встроенной системы рефлексии, как Python или JavaScript. Однако разработчики создали несколько отличных библиотек, которые позволяют:

  • Проверять поля структур на соответствие форматам (email, URL, телефон);
  • Устанавливать ограничения (min, max, required);
  • Создавать кастомные валидаторы для бизнес-логики;
  • Интегрироваться с веб-фреймворками (Gin, Echo, Fiber).

1. Библиотека go-playground/validator (самая популярная)

go-playground/validator — де-факто стандарт для валидации структур в Go. Использует теги (tags) в полях структур и поддерживает более 100 встроенных правил.

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=18,lte=120"`
}

func main() {
    validate := validator.New()
    user := User{
        Name:  "A",
        Email: "invalid-email",
        Age:   15,
    }
    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, Значение: invalid-email
Поле: Age, Ошибка: gte, Значение: 15

2. Библиотека ozzo-validation (альтернатива с цепочками вызовов)

ozzo-validation предлагает более декларативный стиль без тегов. Подходит для случаев, когда структура не может быть изменена или нужна динамическая валидация.

package main

import (
    "fmt"
    "github.com/go-ozzo/ozzo-validation/v4"
    "github.com/go-ozzo/ozzo-validation/v4/is"
)

type Product struct {
    Name  string
    Price float64
}

func main() {
    p := Product{Name: "", Price: -10}
    err := validation.ValidateStruct(&p,
        validation.Field(&p.Name, validation.Required, validation.Length(1, 100)),
        validation.Field(&p.Price, validation.Required, validation.Min(0.01)),
    )
    if err != nil {
        fmt.Println(err)
    }
}

3. Кастомные валидаторы

Часто требуется проверка, не предусмотренная встроенными правилами. Например, валидация пароля или уникальности имени.

package main

import (
    "fmt"
    "github.com/go-playground/validator/v10"
    "unicode"
)

func passwordValidator(fl validator.FieldLevel) bool {
    pwd := fl.Field().String()
    hasUpper := false
    hasDigit := false
    for _, ch := range pwd {
        if unicode.IsUpper(ch) {
            hasUpper = true
        }
        if unicode.IsDigit(ch) {
            hasDigit = true
        }
    }
    return len(pwd) >= 8 && hasUpper && hasDigit
}

type Account struct {
    Password string `validate:"password"`
}

func main() {
    validate := validator.New()
    validate.RegisterValidation("password", passwordValidator)

    acc := Account{Password: "weak"}
    err := validate.Struct(acc)
    if err != nil {
        fmt.Println("Пароль не прошёл валидацию")
    } else {
        fmt.Println("Пароль корректен")
    }
}

4. Валидация HTTP-запросов с Gin

Фреймворк Gin использует go-playground/validator по умолчанию. Это позволяет проверять входящие JSON-данные одной строкой.

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type LoginRequest struct {
    Username string `json:"username" binding:"required,min=3"`
    Password string `json:"password" binding:"required,min=8"`
}

func main() {
    r := gin.Default()
    r.POST("/login", func(c *gin.Context) {
        var req LoginRequest
        if err := c.ShouldBindJSON(&req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        c.JSON(http.StatusOK, gin.H{"message": "Успешный вход"})
    })
    r.Run("                    

Рекомендаций