Валидация данных в 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("