Go-validator наводим порядок во входящих данных без гор if'ов

04 Jun, 2026
19,999
🔱 1,428
👥 121

Привет, коллеги! Сегодня хочу поделиться инструментом, который в свое время сэкономил мне кучу времени и нервов. Давайте поговорим о валидации данных в Go.

Знакомая картина? Вы пишете очередной хендлер для API, и начинается: проверить, что email — это email, пароль не короче 8 символов, а поле age вообще пришло и оно больше 18. Все это выливается в громоздкие цепочки if-else, которые раздувают код и делают его сложным для чтения. А если полей в структуре не пять, а двадцать пять? Караул.

К счастью, есть элегантное решение — библиотека go-playground/validator. Это, без преувеличения, швейцарский нож для валидации структур в Go.

Что это за зверь и зачем он нужен?

Если коротко, validator — это библиотека, которая позволяет описывать правила валидации прямо в тегах полей структуры. Вместо того чтобы писать императивный код "проверь то, потом это", вы декларативно описываете, каким должно быть поле.

Это как если бы вы вместо инструкции по сборке мебели "возьмите винт А, вкрутите в отверстие Б" просто написали на самой доске: "здесь должны быть три винта". Код становится чище, намерения — очевиднее, а поддержка — проще.

Реклама

Библиотека стала де-факто стандартом в Go-сообществе, и не зря. Она даже встроена по умолчанию в популярный веб-фреймворк Gin.

Ключевые возможности, которые вас подкупят

Давайте посмотрим, что делает validator таким мощным инструментом.

1. Декларативная валидация через теги

Это основа основ. Вы просто добавляете тег validate к полям вашей структуры.

type User struct {
    FirstName string `validate:"required"`
    LastName  string `validate:"required"`
    Age       uint8  `validate:"gte=0,lte=130"`
    Email     string `validate:"required,email"`
}

В этом примере мы говорим:

  • FirstName и LastName обязательны для заполнения (required).
  • Age должен быть в диапазоне от 0 до 130 (gte=0, lte=130 — greater/less than or equal).
  • Email должен быть непустым и соответствовать формату email-адреса.

Все. Никаких if'ов в коде хендлера. Просто передаете структуру валидатору и проверяете ошибку.

2. Кросс-филд валидация: когда поля зависят друг от друга

А вот и настоящая магия. Что если одно поле зависит от другого? Классический пример — подтверждение пароля.

type RegisterRequest struct {
    Password        string `validate:"required,min=8"`
    PasswordConfirm string `validate:"required,eqfield=Password"`
}

Тег eqfield=Password говорит сам за себя: значение поля PasswordConfirm должно быть равно значению поля Password. Попробуйте реализовать это вручную — получится не так изящно. А тут — одна строчка.

Библиотека умеет и более сложные сравнения: gtfield (больше чем поле), nefield (не равно полю) и так далее. Это невероятно удобно для сложных бизнес-правил.

3. "Погружение" в слайсы, массивы и мапы

Часто нужно проверить не просто одно поле, а каждый элемент в коллекции. Например, убедиться, что в массиве тегов все строки непустые, или что все email-адреса в списке рассылки валидны.

С validator это делается с помощью тега dive:

type RequestWithLists struct {
    Emails []string `validate:"required,min=1,dive,required,email"`
    Tags   []string `validate:"required,dive,alphanum"`
}

Что здесь происходит?

  • required,min=1: сам слайс Emails должен существовать и содержать хотя бы один элемент.
  • dive: а теперь "ныряем" внутрь.
  • required,email: и каждое значение внутри слайса должно быть непустой строкой и валидным email.

То же самое работает и для мап, причем можно валидировать как ключи, так и значения.

4. Огромная библиотека встроенных правил

Я показал лишь верхушку айсберга. В validator встроены десятки готовых правил на все случаи жизни:

  • Сетевые: ip, ipv4, ipv6, url, uri, mac, cidr.
  • Строковые: contains, startswith, endswith, lowercase, uppercase.
  • Форматы: uuid, credit_card, isbn, datetime, hexcolor, jwt, base64.
  • Сравнения: oneof (значение должно быть одним из перечисленных), gt, lt.
  • Условные: required_if, required_unless (поле обязательно, если другое поле имеет определенное значение).

Этот набор покрывает 99% всех типовых задач валидации. А для оставшегося 1% можно легко написать свои кастомные правила.

5. Кастомизация ошибок и локализация

Стандартные сообщения об ошибках хороши для разработки, но не для конечного пользователя. validator позволяет полностью переопределить тексты ошибок и даже сделать их мультиязычными (i18n). Вы можете настроить вывод так, чтобы вашему фронтенду было удобно показывать пользователю что-то вроде "Поле 'Email' должно быть действующим адресом электронной почты", а не Key: 'User.Email' Error:Field validation for 'Email' failed on the 'email' tag.

Практическое применение: как это выглядит в коде?

Использовать библиотеку очень просто.

  1. Устанавливаем:

    go get github.com/go-playground/validator/v10
    
  2. Используем в коде:

    import "github.com/go-playground/validator/v10"
    
    var validate = validator.New()
    
    func handleRequest(user User) {
        err := validate.Struct(user)
        if err != nil {
            // Ошибки валидации!
            // Здесь мы можем пройтись по ошибкам и вернуть их клиенту
            validationErrors := err.(validator.ValidationErrors)
            // ... обработка validationErrors
            return
        }
        // Данные валидны, можно работать
    }
    

Обратите внимание на обработку ошибок. Валидатор возвращает специальный тип validator.ValidationErrors, который является слайсом ошибок по каждому невалидному полю. Это позволяет гибко обрабатывать результаты и отдавать клиенту информацию по всем проблемным полям сразу.

Стоит ли пробовать?

Однозначно, да. Если вы пишете на Go и вам приходится иметь дело с любыми входящими данными (API запросы, конфигурационные файлы, данные из форм), go-playground/validator станет вашим лучшим другом.

Кому особенно зайдет:

  • Бэкенд-разработчикам: для валидации DTO (Data Transfer Objects) в API-хендлерах.
  • Всем, кто работает с Gin: он уже там, просто начните использовать его на полную.
  • Тем, кто устал от бойлерплейта: библиотека радикально сокращает количество однотипного кода.

Этот проект — прекрасный пример того, как хорошо спроектированный инструмент может сделать код чище, надежнее и проще в поддержке. Не откладывайте, загляните на GitHub и попробуйте его в своем следующем проекте!

🍪 Мы используем файлы cookie и сервис аналитики Яндекс.Метрика, чтобы сайт работал лучше. Продолжая пользоваться devtrends.ru, вы соглашаетесь с обработкой данных согласно Политике конфиденциальности.