validator

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2025 License: MIT Imports: 8 Imported by: 0

README

validator v1.1

Ещё один парсер-валидатор, заточенный под обработку входных параметров JSON API. Радикальная переделка и упрощение версии 1.0.

Подача на вход строки или потока, содержащего JSON. Поучение на выходе any с разобранными данными и формализованного списка ошибок валидации.

N.B. Да, не struct. Но, ИМХО, для построения SQL из заданных в параметрах запросов фильтров map[string]any удобнее.

Контроль типов полей. Nullable-поля, обязательные поля, значения по умолчанию для несуществующих полей и/или полей со значением null. Регистрация ошибок появления в запросе полей имена которых неизвестны валидатору.

Возможность добавления собственных правил валидации (действий) и собственных валидируемых типов данных.

Не реализованы возможности сравнения значений двух полей (пароль и повтор пароля), проверки обязательность одного ( любого) поля в группе полей и прочих зависимостей входных данных. Большая часть подобных правил может быть легко добавлена, но лично мне они пока что без надобности.

Чтобы были понятны задачи, решаемые валидатором, привожу типовое тело запроса (имена полей взяты с потолка, но структура данных реальна и используется в работающих web-api):

{
  "page": {
    "page": 2,
    "size": 50
  },
  "fields": [
    "id",
    "created",
    "age",
    "city"
  ],
  "orders": [
    {
      "field": "age",
      "order": "desc"
    }
  ],
  "filters": {
    "city": {
      "in": [
        "Бийск",
        "Барнаул"
      ]
    },
    "age": {
      ">=": 18,
      "<=": 30
    }
  }
}

Пагинатор, список возвращаемых полей, сортировки, фильтры... Любые из секций могут отсутствовать, вариантов фильтров куда больше.

Зачем?

Накатило... По работе пришлось более плотно заняться сервисами на Go. Так что посмотрел пару популярных валидаторов на аннотациях, код своего предшественника, вернулся к своему говнокоду тех времён, когда механизмов обобщённого программирования в Go ещё не было, сравнил с тем, что сам же делал на PHP, и захотелось сделать очередной велосипед - с блек-джеком и... В общем, приступ графомании, с которым проще не бороться, а написать очередной велосипед - в свободное от основной работы время.

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

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

Скорость инициализации не имеет значения, т.к. валидатор ориентирован на web-сервисы, работающие 24/7.

Принцип работы

Никаких struct и аннотаций.

JSON декодируется в any, содержащий значения nil, string, bool, float64, []any, map[string]any (штатный декодер JSON языка Go), после чего валидатор анализирует эти сырые данные и возвращает any, содержащий значения целевых типов, и регистратор ошибок, содержащий информацию об ошибках входных данных в удобном для возврата из API виде.

N.B. В JavaScript не существует привычных целых чисел: для эмуляции int32 используются float64 с нулевой дробной частью. И декодер JSON в Go следует этому принципу.

Пример

Набросок middleware, разбирающего тело запроса и записывающего результат разбора в контекст:

package api

import (
	"context"
	"encoding/json"
	"net/http"

	_ "github.com/eandr-67/errs"
	v "github.com/eandr-67/validator"
	s "github.com/eandr-67/validator/string"
)

// валидатор
var VL = v.Obj(v.NotNull).
	Field("aaa", v.Int(v.Null, v.Gt[int64](25), v.Le[int64](50))).
	Field("bbb", v.String(v.NotNull, s.Regex("^\\d{5}$"))).
	Required("aaa").Default("bbb", "12345").Compile()

// middleware, использующая валидатор
func GetParams(next http.Handler) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			defer func() { _ = r.Body.Close() }()
			res, err := v.Parse(r.Body, VL) // Разбор тела запроса
			if err != nil {                 // Возвращаем информацию об ошибке
				w.WriteHeader(http.StatusBadRequest)
				_ = json.NewEncoder(w).Encode(err)
				return
			}
			next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "params", res.(map[string]any))))
		},
	)
}

  1. В фабрику Obj, возвращающую построитель валидатора JSON-объекта (map[string]any), передаём действие NotNull, запрещающее значение null.
  2. Вызываем методы построителя Field, добавляющие поля к объекту.
    • В первом случае в метод Field передаём имя поля "aaa" и построитель валидатора целого числа (фабрика Int) c действиями Null (значение null допустимо), Gt (больше), Le (меньше или равно).
    • Во втором случае передаём имя поля "bbb" и построитель валидатора строки (фабрика String) c действиями NotNull и RegExp (проверка соответствия строки заданному регулярному выражению).
  3. Вызываем метод Required, объявляющий поле aaa обязательными.
  4. Вызываем метод Default, задающий для поля bbb значение по умолчанию "12345": если поле bbb отсутствует в JSON, оно будет автоматически создано со значением "12345".
  5. Вызываем метод Compile, собирающий валидатор из полученных построителем валидатора данных.

Таким образом, валидатор проверяет, что JSON содержит объект вида:

{
  "aaa": 37,
  "bbb": "01234"
}

, в котором поле aaa обязательно и должно содержать либо null, либо целое число в диапазоне (25; 50], а поле bbb опционально и должно содержать строку из 5 цифр. Если поле bbb не задано, оно будет создано со значением "12345".

Регистрация ошибок

Для сбора ошибок, возникающих в процессе работы парсера-валидатора, используется модуль регистрации ошибок errs. Он предназначен не специально для валидатора, а для API в целом и реализует унифицированную структуру, удобную для отдачи as is в ответе API - без дополнительных преобразований.

На данный момент валидатор различает 8 видов ошибок, текстовые коды которых (тексты ошибок) сведены в массив ErrMsg. Настройка собственных сообщений об ошибках сводится к изменению значений элементов этого массива.

Для человекочитаемой обработки ошибок создан набор констант с индексами элементов ErrMsg.

Константа Код ошибки по умолчанию Описание ошибки
ErrTypeIncorrect "type" Сырое значение any не может быть преобразовано к требуемому типу
ErrFormatIncorrect "format" Строка имеет ошибочный формат: строка не может быть преобразована к требуемому типу либо строка не соответствует заданному регулярному выражению
ErrLengthIncorrect "length" Длина строки / массива / ассоциативного массива не соответствует заданным условиям
ErrValueIncorrect "value" Значение не соответствует заданным условием
ErrValueIsNull "null" Значение не может быть равно nil
ErrKeyMissed "missed" Отсутствует обязательный ключ ассоциативного массива (обязательное поле объекта JSON)
ErrKeyUnknown "unknown" Встречено неизвестный валидатору ключ ассоциативного массива (неизвестное поле объекта JSON)
ErrPanic "panic[%#v]" Возникновение паники внутри валидатора.

В отличие от других кодов ошибок, представляющих собой просто строки, ошибка ErrPanic - шаблон для Sprintf, в который передаются этот шаблон и recover().

Появление ErrPanic означает, что при создании схемы валидации был допущен логический ляп, скорее всего, связанный с необработкой nil (null). Главный способ минимизации вероятности паники - первым действием каждого валидатора (построителя валидатора) прописывать Null, NotNull или IfNull(...), как это сделано в тестовом примере.

Парсер

Механизм верхнего уровня. Предполагаемое применение (показанное в тестовом примере) - стадия обработки полученных параметров запроса в конвейере middleware.

Получает исходный текстовый JSON и валидатор. Возвращает обработанные данные и регистратор ошибок.

Реализован в модуле в виде двух функций, различающихся источником JSON. Функция:

func Parse(reader io.Reader, vl Validator) (result any, err errs.Errors)

получает JSON из потока ввода, а функция:

func ParseStr(str string, vl Validator) (result any, err errs.Errors)

получает JSON из строки.

Валидатор

N.B. механизмы создания валидаторов описываются ниже - в разделе "Создание валидатора".

Реализует интерфейс:

type Validator interface { Do(raw any) (result any, err errs.Errors) }

Метод Do получает на вход "сырые" данные в виде значения типа any. Возвращает обработанные данные (опять же, в виде значения типа any) и регистратор ошибок.

Работа валидатора начинается запуском преобразователя - функции, преобразующей входное значение типа any к указателю на значение целевого типа. Если преобразователь регистрирует ошибки, работа валидатора завершается.

Иначе полученный указатель подаётся на вход конвейера, обрабатывающего значение. Конвейер состоит из последовательности действий - функций, получающих указатель на значение и возвращающих указатель на обработанное значение того же типа, который передается следующему действию и т.д. Указатель, возвращённый последним выполненным действием, преобразуется в значение, которое обобщается в any и возвращается как результат обработки.

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

Валидация простых значений

Любой валидатор, обрабатывающий значение, не имеющее значимой для процесса валидации внутренней структуры.

Представляет собой простой конвейер, все действия которого явно заданы при создании валидатора.

Любые ошибки записываются в регистратор с ключом "".

Валидация массивов

Преобразователь валидатора массива возвращает указатель на []any.

Конвейер валидатора массива можно разделить на 3 последовательные стадии:

  1. Начальные действия, явно задаваемые при создании валидатора и применяемые к самому массиву.
  2. Автоматически создаваемое скрытое действие, применяющее отдельный валидатор cell к каждому элементу массива.
  3. Конечные действия, явно задаваемые при создании валидатора и применяемые к самому массиву.

Ошибки, возвращаемые cell, записываются в регистратор ошибок валидатора массива, но не останавливают работу конвейера валидатора массива.

Ошибки начальных и конечных действий записываются в регистратор ошибок массива с ключом "". К ключам ошибок, возвращаемых cell, добавляется префикс - индекс ошибочного элемента массива.

N.B. Я сознательно исключил из валидатора поддержку гетерогенных массивов: их использование - очевидная ошибка архитектуры сервиса

Валидация объектов

Преобразователь валидатора массива возвращает указатель на map[string]any

Конвейер валидатора массива можно разделить на 5 последовательных стадий:

  1. Начальные действия, явно задаваемые при создании валидатора и применяемые к самому объекту.
  2. Автоматически создаваемое скрытое действие, проверяющее наличие в объекте полей, объявленных при создании валидатора обязательными. Если обязательное поле не найдено, регистрируется ошибка ErrKeyMissed с ключом - именем поля.
  3. Автоматически создаваемое скрытое действие, автоматически же создающее и инициализирующее отсутствующее в обрабатываемом значении поля, объявленные при создании валидатора автосоздаваемыми.
  4. Автоматически создаваемое скрытое действие, применяющее валидаторы (отдельный валидатор для каждого поля) к каждому существующему полю объекта. Если имя поля неизвестно валидатору, регистрируется ошибка ErrKeyUnknown с ключом - именем поля.
  5. Конечные действия, явно задаваемые при создании валидатора и применяемые к самому объекту.

Ошибки, возвращаемые всеми тремя скрытыми действиями, не останавливают работу конвейера.

Ошибки начальных и конечных действий записываются в регистратор ошибок объекта с ключом "". К ключам ошибок, возвращаемых валидаторами полей, добавляется префикс - имя ошибочного поля.

Валидация uuid.UUID

Вынесена в отдельный пакет validator/uuid, т.к. для реализации UUID использует сторонний пакет uuid. Но, по факту, ничем не отличается от других валидаторов простых значений.

Валидация time.Time

Вынесена в отдельный пакет validator/time.

Во первых, этот тип не является comparable, хотя, по факту, значения time.Time можно не только сравнивать, но и упорядочивать. Но реализовано это не стандартными операциями Go, а методами типа. Потому, для time.Time пришлось создавать свой собственный набор действий.

Во вторых, необходимо было реализовать поддержку шаблонов преобразования string -> time.Time.

В третьих, при работе с датой/временем существует большой геморрой под названием "часовой пояс".

Установка часового пояса

Производится функцией:

func SetTimeZone(tz *time.Location)

Часовой пояс устанавливается глобально - одновременно для всех валидаторов time.Time. Предполагается, что установка будет производиться один раз - при старте приложения, использующего валидатор.

Работа с шаблонами даты/времени

Установка набора используемых шаблонов производится настройкой преобразователя в процессе сборки валидатора построителем. Подробнее в разделе "Построитель валидатора времени/даты".

На всякий случай в пакете задана переменная Default, содержащая наиболее часто используемый в моей работе набор шаблонов.

Преобразователь

Определяется типом:

type Converter[T any] func (raw any, err *errs.Errors) (value *T)

Поучает "сырое" значение типа any и указатель на регистратор ошибок. Возвращает указатель на преобразованное значение.

Если raw == nil, возвращает nil без ошибок. Именно потому используется указатель на значение, а не значение.

Если преобразование невозможно, регистрируется ошибка и возвращается указатель на неинициализированное значение. Исключение - map[string]any, для которого возвращается &map[string]any{}.

Если ошибка произошла при конкретизации any (например, any -> string), регистрируется ошибка ErrTypeIncorrect. Если ошибка произошла при преобразовании промежуточного конкретного типа в целевой тип (например, string -> time.Time), регистрируется ошибка ErrFormatIncorrect.

N.B. Модуль validator не экспортирует преобразователи и при написании кастомных валидаторов необходимо будет самостоятельно реализовать получатель, реализующий сигнатуру типа Conterter.

Действие

Функция - элементарный "кирпичик" процесса обработки значения. Определяется типом:

type Action[T any] func (value *T, err *errs.Errors) (newValue *T, continueProcessing bool)

Получает на вход указатель на обрабатываемое значение и указатель на регистратор ошибок. Возвращает указатель на обработанное значение и флаг продолжения обработки. Если флаг равен true, возвращённое действием значение подаётся на вход следующего действия конвейера. Если флаг равен false, выполнение конвейера досрочно завершается.

Только две функции реализуют непосредственно Action: Null и NotNull. Все остальные действия представляют собой генерирующие функции, получающие на вход набор параметров и возвращающие настроенное этими параметрами замыкание типа Action.

Обобщённые действия реализованы в самом пакете validator, действия, предназначенные для конкретных типов, вынесены в отдельные пакеты.

Действия пакета validator.

Действие Шаблон типа обрабатываемого значения (T) Ошибка Флаг продолжения Описание
Null any value != nil остановка конвейера если nil
NotNull any ErrValueIsNull value != nil ошибка если nil
IfNull(replace T) any true замена nil на replace
Eq(cmp T) comparable ErrValueIncorrect *value == cmp равно
Ne(cmp T) comparable ErrValueIncorrect *value != cmp не равно
In(cmps ...T) comparable ErrValueIncorrect *value in cmps входит в список
NotIn(cmps ...T) comparable ErrValueIncorrect *value not in cmps не входит в список
Lt(cmp T) ordered ErrValueIncorrect *value < cmp меньше
Le(cmp T) ordered ErrValueIncorrect *value <= cmp меньше или равно
Gt(cmp T) ordered ErrValueIncorrect *value > cmp больше
Ge(cmp T) ordered ErrValueIncorrect *value >= cmp больше или равно

Действия пакета validator/string.

Тип обрабатываемого значения - string. Длина строки вычисляется не в байтах, а в символах кодировки UTF-8.

Действие Ошибка Флаг продолжения Описание
Regex(pattern string) ErrFormatIncorrect match(pattern, *value) соответствует регулярке
NotRegex(pattern string) ErrFormatIncorrect !match(pattern, *value) не соответствует регулярке
LenEq(lng int) ErrLengthIncorrect len(*value) == lng длина равна
LenNe(lng int) ErrLengthIncorrect len(*value) != lng длина не равна
LenGe(lng int) ErrLengthIncorrect len(*value) >= lng длина больше или равна
LenLe(lng int) ErrLengthIncorrect len(*value) <= lng длина меньше или равна
LenIn(lngs ...int) ErrLengthIncorrect len(*value) in lngs длина входит в список
LenNotIn(lngs ...int) ErrLengthIncorrect len(*value) not in lngs длина не входит в список

Действия пакета validator/array

Тип обрабатываемого значения - []any.

Действие Ошибка Флаг продолжения Описание
LenEq(lng int) ErrLengthIncorrect len(*value) == lng длина равна
LenNe(lng int) ErrLengthIncorrect len(*value) != lng длина не равна
LenGe(lng int) ErrLengthIncorrect len(*value) >= lng длина больше или равна
LenLe(lng int) ErrLengthIncorrect len(*value) <= lng длина меньше или равна
LenIn(lngs ...int) ErrLengthIncorrect len(*value) in lngs длина входит в список
LenNotIn(lngs ...int) ErrLengthIncorrect len(*value) not in lngs длина не входит в список

Действия пакета validator/time

Тип обрабатываемого значения - time.Time.

Действие Ошибка Флаг продолжения Описание
Eq(cmp time.Time) ErrValueIncorrect *value == cmp равно
Ne(cmp time.Time) ErrValueIncorrect *value != cmp не равно
In(cmps ...time.Time) ErrValueIncorrect *value in cmps входит в список
NotIn(cmps ...time.Time) ErrValueIncorrect *value not in cmps не входит в список
Lt(cmp time.Time) ErrValueIncorrect *value < cmp меньше
Le(cmp time.Time) ErrValueIncorrect *value <= cmp меньше или равно
Gt(cmp time.Time) ErrValueIncorrect *value > cmp больше
Ge(cmp time.Time) ErrValueIncorrect *value >= cmp больше или равно

Создание валидатора

Процесс создания валидатора состоит из 3 этапов:

  1. Создание построителя валидатора.
  2. Задание параметров валидатора - вызовами метода построителя.
  3. Сборка валидатора вызовом метода Compile, рекурсивно вызывающего методы Compile вложенных построителей валидаторов.

Построитель валидатора

Реализует интерфейс:

type Builder interface { Compile() Validator }

Метод Compile собирает валидатор из параметров, переданных построителю.

Создание построителя валидатора производится вызовом фабричной функции, возвращающей новый построитель заданного типа.

Три пакета модуля реализуют фабрики:

Фабричная функция Тип данных построителя/валидатора Исходный тип данных JSON Пакет
String(actions ...Action[string]) *SimpleBuilder[string] string string validator
Bool(actions ...Action[bool]) *SimpleBuilder[bool] bool boolean validator
Float(actions ...Action[float64]) *SimpleBuilder[float64] float64 number validator
Int(actions ...Action[int64]) *SimpleBuilder[int64] int64 number validator
Any(actions ...Action[any]) *SimpleBuilder[any] any любой validator
UUID(actions ...Action[uuid.UUID]) *SimpleBuilder[uuid.UUID] uuid.UUID string validator/uuid
Time(formats []string, actions ...Action[time.Time]) *time.builder time.Time string validator/time
Arr(cell Builder, start ...Action[[]any]) *arrayBuilder []any array validator
Obj(start ...Action[map[string]any]) *objectBuilder map[string]any object validator

Все фабрики, кроме Arr и Time, принимают только набор действий (функций типа Action[T]), которые будут выполнены в начале конвейера обработки значения, и возвращают указатель на построитель валидатора.

Параметры фабрик Arr и Time описываются в разделах, посвящённых построителям валидаторов массивов и даты/времени.

Фабрику Any можно, например, использовать для построения валидатора-пустышки, передающего любые данные с входа на выход без каких-либо преобразований.

Простой построитель (String, Bool, Float, Int, Any, UUID)

Реализован типом SimpleBuilder[T any], собирающим валидатор простых значений. Содержит внутри себя единственный набор действий.

Построитель имеет единственный дополнительный метод:

func (sb *SimpleBuilder[T]) Add(actions ...Action[T]) *SimpleBuilder[T]

Фабрика инициализирует набор действий построителя заданным при вызове фабрики перечислением действий. Вызовы метода Add добавляют перечисленные в вызове действия в конец набора.

Таким образом, валидаторы v1-v9 полностью эквивалентны:

v1 := v.Int(v.Null, v.Gt[int64](25), v.Le[int64](50)).Compile()
v2 := v.Int(v.Null, v.Gt[int64](25), v.Le[int64](50)).Add().Compile()
v3 := v.Int(v.Null, v.Gt[int64](25)).Add(v.Le[int64](50)).Compile()
v4 := v.Int(v.Null).Add(v.Gt[int64](25)).Add(v.Le[int64](50)).Compile()
v5 := v.Int(v.Null).Add(v.Gt[int64](25), v.Le[int64](50)).Compile()
v6 := v.Int().Add(v.Null).Add(v.Gt[int64](25)).Add(v.Le[int64](50)).Compile()
v7 := v.Int().Add(v.Null).Add(v.Gt[int64](25), v.Le[int64](50)).Compile()
v8 := v.Int().Add(v.Null, v.Gt[int64](25)).Add(v.Le[int64](50)).Compile()
v9 := v.Int().Add(v.Null, v.Gt[int64](25), v.Le[int64](50)).Compile()

Построитель валидатора времени/даты.

Реализован типом time.builder.

Фабрика Time отличается тем, что первым параметром получает массив форматов даты/времени, который передаётся в преобразователь создаваемого построителем валидатора.

На этапе разбора string -> time.Time преобразователь time.Time последовательно применяет полученные шаблоны к строке, созданной на этапе any -> string преобразователя - пока один из них не подойдёт. Если все шаблоны вернули ошибку, получатель регистрирует ошибку ErrFormatIncorrect.

В остальном построитель валидатора времени/даты не отличается от простых построителей.

Построитель валидатора массива

Реализован типом arrayBuilder. Содержит внутри себя два набора действий: начальный и конечный.

Фабрика Arr первым параметром получает построитель валидатора элементов массива. Перечисленные после него действия добавляются в начальный набор действий. Конечный набор действий изначально пуст.

Если в качестве построителя валидатора элементов массива передано значение nil, обработка элементов массива производиться будет.

Построитель содержит два дополнительных метода. Метод:

func (ab *arrayBuilder) Start(actions ...Action[[]any]) *arrayBuilder

добавляет перечисленные действия в конец набора начальных действий, а метод:

func (ab *arrayBuilder) Finish(actions ...Action[[]any]) *arrayBuilder

добавляет перечисленные методы в конец набора конечных действий.

Построитель валидатора объекта.

Реализован типом objectBuilder. Содержит внутри себя два набора действий: начальный и конечный, для заполнения которых используются методы

func (ob *objectBuilder) Start(actions ...Action[map[string]any]) *objectBuilder

и

func (ob *objectBuilder) Finish(actions ...Action[map[string]any]) *objectBuilder

, полностью аналогичные одноимённым действиям arrayBuilder.

Помимо Start и Finish построитель валидатора объекта сдержит ещё пять методов, необходимых для настройки трёх скрытых действий.

Добавление полей

Изначально построитель не содержит информации о полях объекта (объект без полей допустим, но имеет мало смысла).

Для добавления поля в построитель валидатора объекта используется метод:

func (ob *objectBuilder) Field(name string, builder Builder) *objectBuilder

В name передаётся имя поля, в builder - построитель валидатора значения этого поля. Если поле с именем name в построителе уже существует, генерируется паника со строкой "field already exists". Если builder = nil, значение поля не проверяется.

Для добавления сразу группы полей используется метод:

func (ob *objectBuilder) FieldList(fields map[string]Builder) *objectBuilder

В fields передаётся ассоциативный массив с ключами - именами полей и значениями - построителями валидаторов этих полей. Внутри себя метод FieldList вызывает метод Field для каждой пары ключ-значение.

Объявление полей обязательными

По умолчанию все поля объекта опциональны и могут быть опущены в исходном JSON. Для того, чтобы объявить набор полей обязательным, используется метод:

func (ob *objectBuilder) Required(fields ...string) *objectBuilder

, получающий набор имён полей и отмечающий эти имена в построителе как обязательные.

Данный метод можно вызывать многократно.

В процессе выполнения Compile() производится проверка того, что имена обязательных полей присутствуют в списке полей, сформированном методами Field и FieldList (если поле не найдено, генерируется паника с текстом "required field does unknown"), и того, что имена обязательных полей отсутствуют в списке полей по умолчанию (если поле присутствует в обоих списках, генерируется паника с текстом "both required and default").

Объявление полей автосоздаваемыми

По умолчанию, если поля нет в исходном JSON, его не будет и в выходных данных валидатора. Но иногда бывает удобно отсутствующие поля создать и инициализировать заданными значениями.

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

Для объявления одного поля автосоздаваемым используется метод:

func (ob *objectBuilder) Default(name string, value any) *objectBuilder

В name передаётся имя поля, в value - инициализирующее это поле значение. Если поле с таким именем уже объявлено автосоздаваемым, генерируется паника с текстом "default already exists".

В процессе выполнения Compile() производится проверка того, что имена автосоздаваемых полей присутствуют в списке полей, сформированном методами Field и FieldList и если поле не найдено, генерируется паника с текстом "default field does unknown".

Для объявления группы полей автосоздаваемыми используется метод:

func (ob *objectBuilder) DefaultList(fields map[string]any) *objectBuilder

Ключи fields - имена полей, значения - инициализаторы этих полей. Внутри себя метод DefaultList вызывает Default для каждой пары ключ-значение.

Создание кастомных валидаторов.

Валидатор произвольного типа создаётся вызовом функции:

func NewValidator[T any](converter Converter[T], actions ...Action[T]) Validator

, получающей преобразователь и перечисление всех действий, составляющих валидатор. Функция возвращает новый валидатор, собранный из переданных параметров. Особенность работы функции в том, что все действия, равные nil, исключаются из конвейера валидатора, что упрощает процесс подготовки набора действий перед его передачей в функцию.

Если converter == nil, генерируется паника со строкой "converter cannot be nil".

Кастомный преобразователь должен соответствовать правилам, перечисленным в разделе "Преобразователь".

В случаях, когда для создания валидатора достаточно функционала простого построителя, можно использовать функцию:

func NewSimpleBuilder[T any](c Converter[T], a ...Action[T]) *SimpleBuilder[T]

, получающую преобразователь и перечисление действий. Функция возвращает новый простой построитель валидатора заданного типа, реализующий метод Add.

Documentation

Index

Constants

View Source
const (
	ErrTypeIncorrect = iota
	ErrFormatIncorrect
	ErrLengthIncorrect
	ErrValueIncorrect
	ErrValueIsNull
	ErrKeyMissed
	ErrKeyUnknown
	ErrPanic
)

Variables

View Source
var ErrMsg = []string{
	"type",
	"format",
	"length",
	"value",
	"null",
	"missed",
	"unknown",
	"panic[%#v]",
}

ErrMsg содержит набор кодов ошибок. В любой момент коды (тексты) ошибок можно заменить на собственные.

Functions

func Arr added in v1.1.0

func Arr(cell Builder, start ...Action[[]any]) *arrayBuilder

Arr фабрика, создающая построитель валидатора типа []any (массив JSON). Получает на вход построитель валидатора, применяемый к элементам массива, и действия, добавляемые в начальный набор. Возвращает указатель на созданный построитель валидатора массива.

Если построитель валидатора элементов не задан (cell == nil), проверка/преобразование элементов массива не производится.

func NotNull

func NotNull[T any](value *T, err *errs.Errors) (*T, bool)

NotNull действие, регистрирующее ошибку ErrValueIsNull, если указатель на значение равен nil.

func Null

func Null[T any](value *T, _ *errs.Errors) (*T, bool)

Null действие, останавливающее конвейер, если указатель на значение равен nil. Не регистрирует ошибки.

Если значение - nil, дальнейшие проверки лишены смысла и лишь приведут к ошибкам.

func Obj added in v1.1.0

func Obj(start ...Action[map[string]any]) *objectBuilder

Obj фабрика, создающая построитель валидатора значения типа map[string]any (объект JSON) Получает на вход действия, добавляемые в начальный набор. Возвращает указатель на созданный построитель валидатора объекта.

func Parse added in v1.1.0

func Parse(reader io.Reader, vl Validator) (result any, err errs.Errors)

Parse декодирует поток reader, содержащий JSON, any и обрабатывает этот any валидатором vl. Возвращает результат обработки и список ошибок.

func ParseStr added in v1.1.0

func ParseStr(str string, vl Validator) (result any, err errs.Errors)

ParseStr декодирует строку str, содержащую JSON, в any и обрабатывает этот any валидатором vl. Возвращает результат обработки и список ошибок.

Types

type Action

type Action[T any] func(value *T, err *errs.Errors) (newValue *T, continueProcessing bool)

Action определяет действие: минимальную единицу валидатора, проверяющую одно условие и, возможно, меняющую проверяемое значение. Получает указатель на проверяемое значение и указатель на регистратор ошибок. Возвращает новое значение и флаг продолжения выполнения набора действий.

Все импортируемые действия, включенные в пакет validator, регистрируют ошибки с ключом "".

func Eq

func Eq[T comparable](cmp T) Action[T]

Eq генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение не равно cmp.

func Ge

func Ge[T cmp.Ordered](cmp T) Action[T]

Ge генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение меньше cmp.

func Gt

func Gt[T cmp.Ordered](cmp T) Action[T]

Gt генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение не больше cmp.

func IfNull

func IfNull[T any](replace T) Action[T]

IfNull генератор действия, возвращающего указатель на replace, если указатель на значение равен nil. Не регистрирует ошибки.

func In

func In[T comparable](cmps ...T) Action[T]

In генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение не входит в набор cmps.

func Le

func Le[T cmp.Ordered](cmp T) Action[T]

Le генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение больше cmp.

func Lt

func Lt[T cmp.Ordered](cmp T) Action[T]

Lt генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение не меньше cmp.

func Ne

func Ne[T comparable](cmp T) Action[T]

Ne генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение равно cmp.

func NotIn

func NotIn[T comparable](cmps ...T) Action[T]

NotIn генератор действия, регистрирующего ошибку ErrValueIncorrect, если значение входит в набор cmps.

type Builder

type Builder interface {
	// Compile генерирует из построителя нетипизированный валидатор.
	Compile() Validator
}

Builder определяет интерфейс, реализуемый построителем.

type Converter

type Converter[T any] func(raw any, err *errs.Errors) (value *T)

Converter определяет преобразователь, получающий значение типа any и возвращающий указатель на значение заданного типа и регистратор ошибок.

Ошибка преобразования регистрируется с ключом "".

type SimpleBuilder added in v1.1.0

type SimpleBuilder[T any] struct {
	// contains filtered or unexported fields
}

SimpleBuilder реализует построитель простого валидатора значения типа T, не содержащего скрытых действий.

func Any added in v1.1.0

func Any(actions ...Action[any]) *SimpleBuilder[any]

Any фабрика, создающая построитель валидатора произвольного типа.

func Bool

func Bool(actions ...Action[bool]) *SimpleBuilder[bool]

Bool фабрика, создающая построитель валидатора логического значения.

func Float

func Float(actions ...Action[float64]) *SimpleBuilder[float64]

Float фабрика, создающая построитель валидатора вещественного числа.

func Int

func Int(actions ...Action[int64]) *SimpleBuilder[int64]

Int фабрика, создающая построитель валидатора целого числа.

func NewSimpleBuilder added in v1.1.0

func NewSimpleBuilder[T any](c Converter[T], a ...Action[T]) *SimpleBuilder[T]

NewSimpleBuilder создаёт построитель простого валидатора, обрабатывающего значения без учёта их внутренней структуры. Получает преобразователь и набор действий. Возвращает указатель на созданный построитель.

func String

func String(actions ...Action[string]) *SimpleBuilder[string]

String фабрика, создающая построитель валидатора строки.

func (*SimpleBuilder[T]) Add added in v1.1.0

func (sb *SimpleBuilder[T]) Add(actions ...Action[T]) *SimpleBuilder[T]

Add добавляет действия в набор. Возвращает указатель на построитель для удобной организации цепочек вызовов методов.

func (*SimpleBuilder[T]) Compile added in v1.1.0

func (sb *SimpleBuilder[T]) Compile() Validator

Compile производит сборку валидатора из установленных параметров построителя

type Validator

type Validator interface {
	// Do выполняет собственно процесс валидации.
	Do(raw any) (result any, err errs.Errors)
}

Validator определяет нетипизированный (не содержит информацию о типе проверяемого значения) интерфейс, реализуемый каждым валидатором. Получает на вход сырые данные типа any и возвращает обработанные данные типа any и регистратор ошибок. Если ошибок нет, в err возвращается nil.

func NewValidator added in v1.1.0

func NewValidator[T any](converter Converter[T], actions ...Action[T]) Validator

NewValidator создаёт валидатор. Получает на вход преобразователь и набор действий и возвращает валидатор. В валидатор включаются только действия, не равные nil.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL