Валидатор ежедневной библиотеки Go

Go

Введение

Сегодня мы представляем очень полезную библиотеку -validator.validatorИспользуется для проверки данных. В веб-разработке нам необходимо строго проверять данные, отправляемые пользователями, чтобы предотвратить вредоносные запросы от пользователей. Например, формат даты, возраст пользователя, пол и т. д. должны быть нормальными значениями и не могут быть установлены произвольно.

быстрый в использовании

Сначала установите:

$ go get gopkg.in/go-playground/validator.v10

После использования:

package main

import (
  "fmt"

  "gopkg.in/go-playground/validator.v10"
)

type User struct {
  Name string `validate:"min=6,max=10"`
  Age  int    `validate:"min=1,max=100"`
}

func main() {
  validate := validator.New()

  u1 := User{Name: "lidajun", Age: 18}
  err := validate.Struct(u1)
  fmt.Println(err)

  u2 := User{Name: "dj", Age: 101}
  err = validate.Struct(u2)
  fmt.Println(err)
}

validatorв теге структуры (struct tag) полей, определенных вограничение. использоватьvalidatorПеред проверкой данных нам нужно вызватьvalidator.New()Создаватьвалидатор, этот валидатор может задавать параметры, добавлять пользовательские ограничения, а затем вызыватьStruct()методы для проверки того, что поля различных структурных объектов соответствуют определенным ограничениям.

В приведенном выше коде мы определяем структуруUser,Userимеет имяNameполе и возрастAgeполе. пройти черезminиmaxограничения, мы устанавливаемNameДлина строки[6,10]между,AgeДиапазон[1,100].

первый объектNameиAgeполя удовлетворяют ограничениям, поэтомуStruct()метод возвращаетnilОшибка.第二个对象的NameЗначение поляdj, длина 2, менее минmin,AgeЗначение поля равно 101, что больше максимального значения.max, поэтому возвращается ошибка:

<nil>
Key: 'User.Name' Error:Field validation for 'Name' failed on the 'min' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'max' tag

Сообщения об ошибках легче понять,User.Nameломатьminограничение,User.AgeломатьmaxОграничения, вы можете увидеть проблему с первого взгляда.

Уведомление:

  • validatorОн был обновлен и повторен во многих версиях, текущая последняя версияv10, между различными версиями есть некоторые различия, каждый должен обращать внимание на различие при использовании и чтении кода. Я использую последнюю версию здесьv10Как демо-версия;
  • Можно передавать как длину строки, так и диапазон значенийminиmaxограничить.

ограничение

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

ограничения области

Мы видели выше, что использованиеminиmaxЧтобы ограничить длину строки или диапазон значений, ниже будут введены другие ограничения диапазона. Типы полей для ограничений диапазона следующие:

  • Для числовых значений ограничьте его значение;
  • Для строк длина ограничена;
  • Для срезов, массивов иmap, ограничить его длину.

Если не указано иное, значения, соответствующие вышеуказанным типам, сравниваются со значениями параметров.

  • len: равно значению параметра, например.len=10;
  • max: меньше или равно значению параметра, напримерmax=10;
  • min: больше или равно значению параметра, напримерmin=10;
  • eq: равно значению параметра, обратите внимание наlenразные. Для струн,eqЗначение ограничения самой строки,len约束字符串长度。 Напримерeq=10;
  • ne: не равно значению параметра, например.ne=10;
  • gt: больше, чем значение параметра, например.gt=10;
  • gte: больше или равно значению параметра, напримерgte=10;
  • lt: меньше значения параметра, например.lt=10;
  • lte: меньше или равно значению параметра, напримерlte=10;
  • oneof: Может быть только одно из перечисленных значений.Эти значения должны быть числами или строками, разделенными пробелами.Если в строке есть пробелы,заключите строку в одинарные кавычки,напримерoneof=red green.

Большинство из нем вполне интуитивно понятно, мы посмотрим на примере, как использовать несколько ограничений:

type User struct {
  Name    string    `validate:"ne=admin"`
  Age     int       `validate:"gte=18"`
  Sex     string    `validate:"oneof=male female"`
  RegTime time.Time `validate:"lte"`
}

func main() {
  validate := validator.New()

  u1 := User{Name: "dj", Age: 18, Sex: "male", RegTime: time.Now().UTC()}
  err := validate.Struct(u1)
  if err != nil {
    fmt.Println(err)
  }

  u2 := User{Name: "admin", Age: 15, Sex: "none", RegTime: time.Now().UTC().Add(1 * time.Hour)}
  err = validate.Struct(u2)
  if err != nil {
    fmt.Println(err)
  }
}

В приведенном выше примере мы определилиUserобъект с ограничениями, установленными для его 4 полей:

  • Name: Строка не может бытьadmin;
  • Age: должно быть больше или равно 18,Несовершеннолетним вход воспрещен;
  • SexГендер должен бытьmaleиfemaleодин из;
  • RegTime: время регистрации должно быть меньше текущего времени UTC, обратите внимание, что если тип поляtime.Time,использоватьgt/gte/lt/lteНет необходимости указывать значение параметра, когда ограничение равно, и по умолчанию оно сравнивается с текущим временем UTC.

Точно так же поле первого объекта является законным, проверено. Четыре поля второго объекта являются незаконными, а выходная информация очень четко определена:

Key: 'User.Name' Error:Field validation for 'Name' failed on the 'ne' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'gte' tag
Key: 'User.Sex' Error:Field validation for 'Sex' failed on the 'oneof' tag
Key: 'User.RegTime' Error:Field validation for 'RegTime' failed on the 'lte' tag

Ограничения по полям

validatorПозволяет определить ограничения для полей, т. е. отношения между этим полем и другими полями. Это ограничение на самом деле делится на два типа: один состоит в том, что поле параметра является плоским полем в той же структуре, а другой заключается в том, что поле параметра является полем других полей в структуре. Синтаксис ограничения прост и требует лишь небольшой модификации, чтобы использовать описанную выше семантику ограничения. Напримерограничение равенства(eq), если нужно ограничить поля одной и той же структурой, добавьтеfield,использоватьeqfieldОпределите ограничения равенства между полями. Если это более глубокое поле, вfieldНужно добавить передcs(можно понимать какcross-struct),eqстановитсяeqcsfield. Их значения параметров — это все имена полей, которые необходимо сравнить, а внутреннему слою также необходимо добавить тип поля:

eqfield=ConfirmPassword
eqcsfield=InnerStructField.Field

См. пример:

type RegisterForm struct {
  Name      string `validate:"min=2"`
  Age       int    `validate:"min=18"`
  Password  string `validate:"min=10"`
  Password2 string `validate:"eqfield=Password"`
}

func main() {
  validate := validator.New()

  f1 := RegisterForm{
    Name:      "dj",
    Age:       18,
    Password:  "1234567890",
    Password2: "1234567890",
  }
  err := validate.Struct(f1)
  if err != nil {
    fmt.Println(err)
  }

  f2 := RegisterForm{
    Name:      "dj",
    Age:       18,
    Password:  "1234567890",
    Password2: "123",
  }
  err = validate.Struct(f2)
  if err != nil {
    fmt.Println(err)
  }
}

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

Key: 'RegisterForm.Password2' Error:Field validation for 'Password2' failed on the 'eqfield' tag

Нить

validatorОграничений на струну много, вот некоторые из них:

  • contains=: содержит подстроку параметра, например.contains=email;
  • containsany: содержит любые символы UNICODE в параметре, например.containsany=abcd;
  • containsrune: содержит символ руны, представленный параметром, например.containsrune=☻;
  • excludes: не содержит подстрок параметров, например.excludes=email;
  • excludesall: не содержит символов UNICODE в параметре, например.excludesall=abcd;
  • excludesrune: не содержит символа руны, представленного параметром,excludesrune=☻;
  • startswith: Параметры подстроки префикса, напримерstartswith=hello;
  • endswith: с суффиксом подстроки параметра, например.endswith=bye.

См. пример:

type User struct {
  Name string `validate:"containsrune=☻"`
  Age  int    `validate:"min=18"`
}

func main() {
  validate := validator.New()

  u1 := User{"d☻j", 18}
  err := validate.Struct(u1)
  if err != nil {
    fmt.Println(err)
  }

  u2 := User{"dj", 18}
  err = validate.Struct(u2)
  if err != nil {
    fmt.Println(err)
  }
}

ограничениеNameПоле должно содержать символы UNICODE.

уникальность

использоватьunqiueЧтобы указать уникальные ограничения, различные типы обрабатываются следующим образом:

  • Для массивов и срезовuniqueОграничение на отсутствие повторяющихся элементов;
  • заmap,uniqueОграничения не дублируютсяценность;
  • Для срезов, тип элемента которых struct,uniqueПоле объекта структуры ограничений не повторяется, т.к.unqiue=fieldУкажите имя этого поля.

Например:

type User struct {
  Name    string   `validate:"min=2"`
  Age     int      `validate:"min=18"`
  Hobbies []string `validate:"unique"`
  Friends []User   `validate:"unique=Name"`
}

func main() {
  validate := validator.New()

  f1 := User{
    Name: "dj2",
    Age:  18,
  }
  f2 := User{
    Name: "dj3",
    Age:  18,
  }

  u1 := User{
    Name:    "dj",
    Age:     18,
    Hobbies: []string{"pingpong", "chess", "programming"},
    Friends: []User{f1, f2},
  }
  err := validate.Struct(u1)
  if err != nil {
    fmt.Println(err)
  }

  u2 := User{
    Name:    "dj",
    Age:     18,
    Hobbies: []string{"programming", "programming"},
    Friends: []User{f1, f1},
  }
  err = validate.Struct(u2)
  if err != nil {
    fmt.Println(err)
  }
}

Мы ограничиваем хоббиHobbiesне может иметь дубликаты элементов в нем, другFriendsОтдельные элементы не могут иметь одинаковое имяName. Первый объект удовлетворяет ограничению, второй объектHobbiesполе содержит дубликаты"programming",Friendsиз двух элементов поляNameполяdj2. Вывод программы:

Key: 'User.Hobbies' Error:Field validation for 'Hobbies' failed on the 'unique' tag
Key: 'User.Friends' Error:Field validation for 'Friends' failed on the 'unique' tag

Почта

пройти черезemailПоле ограничения должно быть в почтовом формате:

type User struct {
  Name  string `validate:"min=2"`
  Age   int    `validate:"min=18"`
  Email string `validate:"email"`
}

func main() {
  validate := validator.New()

  u1 := User{
    Name:  "dj",
    Age:   18,
    Email: "dj@example.com",
  }
  err := validate.Struct(u1)
  if err != nil {
    fmt.Println(err)
  }

  u2 := User{
    Name:  "dj",
    Age:   18,
    Email: "djexample.com",
  }
  err = validate.Struct(u2)
  if err != nil {
    fmt.Println(err)
  }
}

Выше мы ограничиваемEmailПоле должно быть в формате почты, первый объект удовлетворяет ограничению, второй объект нет, программа выводит:

Key: 'User.Email' Error:Field validation for 'Email' failed on the 'email' tag

специальный

Есть несколько специальных ограничений:

  • -: Пропустите это поле без проверки;
  • |: При использовании нескольких ограничений необходимо удовлетворить только одно из них, например.rgb|rgba;
  • required: поле должно быть установлено и не может быть значением по умолчанию;
  • omitempty: Если поле не задано, оно игнорируется.

разное

validatorПредоставляет большое количество различных аспектов, богатых ограничений, таких какASCII/UNICODEБуквы, цифры, шестнадцатеричное, шестнадцатеричное значение цвета, регистр, значение цвета RBG, значение цвета HSL, значение цвета HSLA, формат JSON, путь к файлу, URL-адрес, строка в кодировке base64, IP-адрес, ipv4, ipv6, UUID, широта и долгота и т. д. ., и т.д., и т.д., и т.п. Из-за нехватки места мы не будем вводить их здесь по одному. Интересно покопаться в документации самостоятельно.

VarWithValueметод

В некоторых очень простых случаях мы просто хотим сравнить две переменные, если мы сначала определим структуру иtagЭто слишком сложно.validatorпри условииVarWithValue()метод, нам нужно только передать две переменные и ограничения для проверки

func main() {
  name1 := "dj"
  name2 := "dj2"

  validate := validator.New()
  fmt.Println(validate.VarWithValue(name1, name2, "eqfield"))

  fmt.Println(validate.VarWithValue(name1, name2, "nefield"))
}

пользовательские ограничения

Помимо использованияvalidatorПредоставьте ограничения, вы также можете определить свои собственные ограничения. Теперь есть замечательный пример спроса, продукт требует, чтобы пользователи использовали строку палиндрома студентов в качестве имени пользователя, мы можем настроить это ограничение:

type RegisterForm struct {
  Name string `validate:"palindrome"`
  Age  int    `validate:"min=18"`
}

func reverseString(s string) string {
  runes := []rune(s)
  for from, to := 0, len(runes)-1; from < to; from, to = from+1, to-1 {
    runes[from], runes[to] = runes[to], runes[from]
  }

  return string(runes)
}

func CheckPalindrome(fl validator.FieldLevel) bool {
  value := fl.Field().String()
  return value == reverseString(value)
}

func main() {
  validate := validator.New()
  validate.RegisterValidation("palindrome", CheckPalindrome)

  f1 := RegisterForm{
    Name: "djd",
    Age:  18,
  }
  err := validate.Struct(f1)
  if err != nil {
    fmt.Println(err)
  }

  f2 := RegisterForm{
    Name: "dj",
    Age:  18,
  }
  err = validate.Struct(f2)
  if err != nil {
    fmt.Println(err)
  }
}

Сначала определите тип какfunc (validator.FieldLevel) boolФункция проверяет, выполняются ли ограничения, что можно сделать с помощьюFieldLevelПолучите информацию о поле для проверки. Затем позвоните валидаторуRegisterValidation()метод регистрирует ограничение на указанное имя. Наконец, мы можем использовать ограничение в структуре. В приведенной выше программе второй объект не удовлетворяет ограничениюpalindrome, вывод:

Key: 'RegisterForm.Name' Error:Field validation for 'Name' failed on the 'palindrome' tag

обработка ошибок

В приведенном выше примере мы просто выводим ошибку, возвращаемую при сбое проверки. На самом деле, мы можем сделать более точную обработку.validatorНа самом деле возвращаются только две ошибки: одна — ошибка параметра, а другая — ошибка проверки. Если параметр неверный, возвратInvalidValidationErrorТип; возвращает, если возникает ошибка проверкиValidationErrors, все они достигаютerrorинтерфейс. иValidationErrorsпредставляет собой срез ошибок, который содержит информацию о каждом нарушении ограничения каждым полем:

// src/gopkg.in/validator.v10/errors.go
type InvalidValidationError struct {
  Type reflect.Type
}

// Error returns InvalidValidationError message
func (e *InvalidValidationError) Error() string {
  if e.Type == nil {
    return "validator: (nil)"
  }

  return "validator: (nil " + e.Type.String() + ")"
}

type ValidationErrors []FieldError

func (ve ValidationErrors) Error() string {
  buff := bytes.NewBufferString("")
  var fe *fieldError

  for i := 0; i < len(ve); i++ {
    fe = ve[i].(*fieldError)
    buff.WriteString(fe.Error())
    buff.WriteString("\n")
  }
  return strings.TrimSpace(buff.String())
}

такvalidatorВсего 3 случая результата, возвращаемого проверкой:

  • nil: нет ошибок;
  • InvalidValidationError: ошибка входного параметра;
  • ValidationErrors: Поле нарушает ограничения.

Можем судить в программеerr != nil, в свою очередьerrпреобразовать вInvalidValidationErrorиValidationErrorsдля получения более подробной информации:

func processErr(err error) {
  if err == nil {
    return
  }

  invalid, ok := err.(*validator.InvalidValidationError)
  if ok {
    fmt.Println("param error:", invalid)
    return
  }

  validationErrs := err.(validator.ValidationErrors)
  for _, validationErr := range validationErrs {
    fmt.Println(validationErr)
  }
}

func main() {
  validate := validator.New()

  err := validate.Struct(1)
  processErr(err)

  err = validate.VarWithValue(1, 2, "eqfield")
  processErr(err)
}

Суммировать

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

Если вы найдете забавную и простую в использовании языковую библиотеку Go, вы можете отправить сообщение о проблеме в ежедневной библиотеке Go GitHub😄

Ссылаться на

  1. валидатор гитхаб:GitHub.com/go-play dog...
  2. Перейти на ежедневный репозиторий GitHub:GitHub.com/Darenjun/go-of…

я

мой блог:darjun.github.io

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~