Skip to content

Validator Validierungsbibliothek

Offizielle Adresse: go-playground/validator: 💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving (github.com)

Dokumentation: validator/README.md at master · go-playground/validator (github.com)

Offizielle Beispiele: validator/_examples at master · go-playground/validator (github.com)

Benchmark-Tests: go-playground/validator: 💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving (github.com)

Einführung

go-playground/validator implementiert einen auf Struktur-Tags basierenden Wert-Validator mit folgenden einzigartigen Eigenschaften:

  • Feld-übergreifende und strukturübergreifende Validierung mit Validierungs-Tags und benutzerdefinierten Validatoren

  • Slices, Arrays, Maps oder beliebige mehrdimensionale Felder können validiert werden

  • Map-Schlüssel und -Werte können tiefgehend validiert werden

  • Vor der Validierung wird anhand des Basistyps bestimmt, wie verarbeitet werden soll

  • Benutzerdefinierte Feldtypen können verarbeitet werden

  • Unterstützt Alias-Tags, die mehrere Validierungen einem einzelnen Tag zuordnen, um die Validierung von Strukturen einfacher zu definieren

  • Benutzerdefinierte Feldnamen können extrahiert werden, z.B. kann der JSON-Name bei der Validierung extrahiert werden, um in Fehlermeldungen angezeigt zu werden

  • Benutzerdefinierte mehrsprachige Fehlermeldungen

  • Standardmäßige Validierungskomponente des gin Frameworks

Installation

powershell
go get github.com/go-playground/validator/v10

Import

go
import "github.com/go-playground/validator/v10"

Tags

Der Validator verfügt über sehr viele Basis-Validierungs-Tags. Alle Validierungsfunktionen der Tags können in der Datei baked_in.go gefunden werden. Das Struktur-Tag des Validators ist validate,

Beispiel:

go
type User {
  age int `validate:"gte=18"` // bedeutet größer oder gleich 18 Jahre
}

Das Standard-Tag kann auch mit der setTagName-Methode geändert werden.

Felder

TagBeschreibung
eqcsfieldValidiert in einer separaten Struktur, ob der Wert des aktuellen Feldes gleich dem durch den Parameterwert angegebenen Feld ist
eqfieldValidiert, ob der Wert des aktuellen Feldes gleich dem durch den Parameterwert angegebenen Feld ist
fieldcontainsValidiert, ob der Wert des aktuellen Feldes das durch den Parameterwert angegebene Feld enthält
fieldexcludesValidiert, ob der Wert des aktuellen Feldes das durch den Parameterwert angegebene Feld nicht enthält
gtcsfieldValidiert in einer separaten Struktur, ob der Wert des aktuellen Feldes größer ist als das durch den Parameterwert angegebene Feld
gtecsfieldValidiert in einer separaten Struktur, ob der Wert des aktuellen Feldes größer oder gleich dem durch den Parameterwert angegebenen Feld ist
gtefieldValidiert, ob der Wert des aktuellen Feldes größer oder gleich dem durch den Parameterwert angegebenen Feld ist
gtfieldValidiert, ob der Wert des aktuellen Feldes größer ist als das durch den Parameterwert angegebene Feld
ltcsfieldValidiert in einer separaten Struktur, ob der Wert des aktuellen Feldes kleiner ist als das durch den Parameterwert angegebene Feld
ltecsfieldValidiert in einer separaten Struktur, ob der Wert des aktuellen Feldes kleiner oder gleich dem durch den Parameterwert angegebenen Feld ist
ltefieldValidiert, ob der Wert des aktuellen Feldes kleiner oder gleich dem durch den Parameterwert angegebenen Feld ist
ltfieldValidiert, ob der Wert des aktuellen Feldes kleiner ist als das durch den Parameterwert angegebene Feld
necsfieldValidiert, dass der Wert des aktuellen Feldes nicht gleich dem Feld in der separaten Struktur ist, das durch den Parameterwert angegeben wird
nefieldValidiert, ob der Wert des aktuellen Feldes ungleich dem durch den Parameterwert angegebenen Feld ist

Netzwerk

TagBeschreibung
cidrClassless Inter-Domain Routing CIDR
cidrv4Classless Inter-Domain Routing CIDRv4
cidrv6Classless Inter-Domain Routing CIDRv6
datauriData Uniform Resource Locator
fqdnFully Qualified Domain Name (FQDN)
hostnameHostname RFC 952
hostname_portFeldvalidierung für Socket-Adresse<dns>:<port> Kombination
hostname_rfc1123Hostname RFC 952
ipInternet Protocol Address IP
ip4_addrInternet Protocol Address IPv4
ip6_addrInternet Protocol Address IPv6
ip_addrInternet Protocol Address IP
ipv4Internet Protocol Address IPv4
ipv6Internet Protocol Address IPv6
macMedia Access Control Address, auch LAN-Adresse genannt
tcp4_addrTransmission Control Protocol Address TCP4
tcp6_addrTransmission Control Protocol Address TCPv6
tcp_addrTransmission Control Protocol Address TCP
udp4_addrUser Datagram Protocol Address UDPv4
udp6_addrUser Datagram Protocol Address UDPv6
udp_addrUser Datagram Protocol Address UDP
unix_addrUnix Domain Socket Endpunkt-Adresse
uriUniform Resource Identifier
urlUniform Resource Locator
url_encodedUniform Resource Identifier Codierung
urn_rfc2141RFC 2141 Uniform Resource Name

Zeichenfolgen

TagBeschreibung
alphaValidiert, ob der Wert des aktuellen Feldes gültige Buchstaben sind
alphanumValidiert, ob der Wert des aktuellen Feldes gültige alphanumerische Zeichen sind
alphanumunicodeValidiert, ob der Wert des aktuellen Feldes gültige alphanumerische Unicode-Zeichen ist
alphaunicodeValidiert, ob der Wert des aktuellen Feldes gültige Unicode-Buchstaben ist
asciiValidiert, ob der Wert des Feldes ein gültiges ASCII-Zeichen ist
booleanValidiert, ob der Wert des aktuellen Feldes ein gültiger boolescher Wert ist oder sicher in einen booleschen Wert konvertiert werden kann
containsValidiert, ob der Wert des Feldes den im Parameter angegebenen Text enthält
containsanyValidiert, ob der Wert des Feldes eines der im Parameter angegebenen Zeichen enthält
containsruneValidiert, ob der Wert des Feldes das im Parameter angegebene Rune enthält
endsnotwithValidiert, dass der Wert des Feldes nicht mit dem im Parameter angegebenen Text endet
endswithValidiert, dass der Wert des Feldes mit dem im Parameter angegebenen Text endet
excludesValidiert, dass der Wert des Feldes den im Parameter angegebenen Text nicht enthält
excludesallValidiert, dass der Wert des Feldes keines der im Parameter angegebenen Zeichen enthält
excludesruneValidiert, dass der Wert des Feldes das im Parameter angegebene Zeichen nicht enthält
lowercaseValidiert, ob der Wert des aktuellen Feldes eine Kleinbuchstaben-Zeichenkette ist
multibyteValidiert, ob der Wert des Feldes Mehrbyte-Zeichen hat
numberValidiert, ob der Wert des aktuellen Feldes eine gültige Zahl ist
numericValidiert, ob der Wert des aktuellen Feldes ein gültiger numerischer Wert ist
printasciiValidiert, ob der Wert des Feldes ein gültiges druckbares ASCII-Zeichen ist
startsnotwithValidiert, dass der Wert des Feldes nicht mit dem im Parameter angegebenen Text beginnt
startswithValidiert, ob der Wert des Feldes mit dem im Parameter angegebenen Text beginnt
uppercaseValidiert, ob der Wert des aktuellen Feldes eine Großbuchstaben-Zeichenkette ist

Formatierung

TagBeschreibung
base64Base64-Zeichenkette
base64urlBase64URL-Zeichenkette
bicValidiert, ob der Wert des aktuellen Feldes ein gültiger BIC-Code (SWIFT-Code) nach ISO 9362 ist
bcp47_language_tagValidiert, ob der Wert des aktuellen Feldes ein Sprach-Tag nach BCP47-Spezifikation ist
btc_addrValidiert, ob der Wert des Feldes eine gültige BTC-Adresse ist
btc_addr_bech32Validiert, ob der Wert des Feldes eine gültige bech32 BTC-Adresse ist
credit_cardValidiert, ob der Wert des aktuellen Feldes eine gültige Kreditkartennummer ist
datetimeValidiert, ob der Wert des aktuellen Feldes eine gültige Datums-Zeichenkette ist
e164Validiert, ob der Wert des aktuellen Feldes eine gültige Telefonnummer im E.164-Format ist
emailValidiert, ob der Wert des aktuellen Feldes eine gültige E-Mail-Adresse ist
eth_addrValidiert, ob der Wert des Feldes eine gültige Ethereum-Adresse ist
hexadecimalValidiert, ob der Wert des aktuellen Feldes gültig hexadezimal ist
hexcolorValidiert, ob der Wert des aktuellen Feldes eine gültige Hexadezimal-Farbe ist
hslValidiert, ob der Wert des aktuellen Feldes eine gültige HSL-Farbe ist
hslaValidiert, ob der Wert des aktuellen Feldes eine gültige HSLA-Farbe ist
htmlValidiert, ob der Wert des aktuellen Feldes gültiges HTML ist
html_encodedValidiert, ob der Wert des aktuellen Feldes gültiges HTML-codiert ist
isbnValidiert, ob der Wert des Feldes eine gültige v10 oder v13 ISBN (Internationale Standardbuchnummer) ist
isbn10Validiert, ob der Wert des Feldes eine gültige v10 ISBN (Internationale Standardbuchnummer) ist
isbn13Validiert, ob der Wert des Feldes eine gültige v13 ISBN (Internationale Standardbuchnummer) ist
iso3166_1_alpha2Validiert, ob der Wert des aktuellen Feldes ein gültiger iso3166-1 Alpha-2 Ländercode ist
iso3166_1_alpha3Validiert, ob der Wert des aktuellen Feldes ein gültiger iso3166-1 Alpha-3 Ländercode ist
iso3166_1_alpha_numericValidiert, ob der Wert des aktuellen Feldes ein gültiger iso3166-1 alphanumerischer Ländercode ist
iso3166_2Validiert, ob der Wert des aktuellen Feldes ein gültischer Länderregionscode (ISO 3166-2) ist
iso4217Validiert, ob der Wert des aktuellen Feldes ein gültiger Währungscode (ISO 4217) ist
jsonValidiert, ob der Wert des aktuellen Feldes eine gültige JSON-Zeichenkette ist
jwtValidiert, ob der Wert des aktuellen Feldes eine gültige JWT-Zeichenkette ist
latitudeValidiert, ob der Wert des Feldes gültige Breitenkoordinaten ist
longitudeValidiert, ob der Wert des Feldes gültige Längenkoordinaten ist
postcode_iso3166_alpha2Validiert basierend auf dem Ländercode-Wert in ISO 3166 Alpha 2
postcode_iso3166_alpha2_fieldValidiert durch ein Feld, das den Ländercode-Wert in ISO 3166 Alpha 2 darstellt
rgbValidiert, ob der Wert des aktuellen Feldes eine gültige RGB-Farbe ist
rgbaValidiert, ob der Wert des aktuellen Feldes eine gültige RGBA-Farbe ist
ssnValidiert, ob der Wert des Feldes eine gültige SSN ist
timezoneValidiert, ob der Wert des aktuellen Feldes eine gültige Zeitzonen-Zeichenkette ist
uuidValidiert, ob der Wert des Feldes eine gültige UUID einer beliebigen Version ist
uuid3Validiert, ob der Wert des Feldes eine gültige UUID v3 ist
uuid3_rfc4122Validiert, ob der Wert des Feldes eine gültige RFC4122 v3 UUID ist
uuid4Validiert, ob der Wert des Feldes eine gültige v4 UUID ist
uuid4_rfc4122Validiert, ob der Wert des Feldes eine gültige RFC4122 v4 UUID ist
uuid5Validiert, ob der Wert des Feldes eine gültige v5 UUID ist
uuid5_rfc4122Validiert, ob der Wert des Feldes eine gültige RFC4122 v5 UUID ist
uuid_rfc4122Validiert, ob der Wert des Feldes eine gültige RFC4122 UUID einer beliebigen Version ist
md4Validiert, ob der Wert des Feldes ein gültiges MD4 ist
md5Validiert, ob der Wert des Feldes ein gültiges MD5 ist
sha256Validiert, ob der Wert des Feldes ein gültiges SHA256 ist
sha384Validiert, ob der Wert des Feldes ein gültiges SHA384 ist
sha512Validiert, ob der Wert des Feldes ein gültiges SHA512 ist
ripemd128Validiert, ob der Wert des Feldes ein gültiges PIPEMD128 ist
ripemd128Validiert, ob der Wert des Feldes ein gültiges PIPEMD160 ist
tiger128Validiert, ob der Wert des Feldes ein gültiges TIGER128 ist
tiger160Validiert, ob der Wert des Feldes ein gültiges TIGER160 ist
tiger192Validiert, ob der Wert des Feldes ein gültiges TIGER192 ist
semverValidiert, ob der Wert des aktuellen Feldes eine gültige Semver-Version nach Semantic Versioning 2.0.0 ist
ulidValidiert, ob der Wert des Feldes eine gültige ULID ist

Vergleich

TagBeschreibung
eqGleich
gtGrößer
gteGrößer gleich
ltKleiner
lteKleiner gleich
neUngleich

Sonstige

TagBeschreibung
dirVerzeichnis
fileDateipfad
isdefaultValidiert, ob der Wert des aktuellen Feldes der Standard-Statikwert ist
lenFeldlänge
maxMaximalwert
minMinimalwert
oneofOb einer der aufgelisteten Werte
oimtemptyWenn das Feld nicht gesetzt ist, wird das Feld ignoriert
requiredPflichtwert
required_ifNur wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, muss das Validierungsfeld existieren und darf nicht leer sein
required_unlessAußer wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, muss das Validierungsfeld existieren und darf nicht leer sein
required_withWenn eines der angegebenen Felder existiert, muss das Validierungsfeld existieren und darf nicht leer sein
required_with_allWenn alle angegebenen Felder existieren, muss das Validierungsfeld existieren und darf nicht leer sein
required_withoutWenn eines der angegebenen Felder nicht existiert, muss das Validierungsfeld existieren und darf nicht leer sein
required_without_allWenn keines der angegebenen Felder existiert, muss das Validierungsfeld existieren und darf nicht leer sein
excluded_ifNur wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, darf das Validierungsfeld nicht existieren oder leer sein
excluded_unlessAußer wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, darf das Validierungsfeld nicht existieren oder leer sein
excluded_withWenn eines der angegebenen Felder existiert, darf das Validierungsfeld nicht existieren oder leer sein
excluded_with_allWenn alle angegebenen Felder existieren, darf das Validierungsfeld nicht existieren oder leer sein
excluded_withoutWenn eines der angegebenen Felder nicht existiert, darf das Validierungsfeld nicht existieren oder leer sein
excluded_without_allWenn keines der angegebenen Felder existiert, darf das Validierungsfeld nicht existieren oder leer sein
uniqueValidiert, ob jeder arr-, map-, slice-Wert eindeutig ist

Aliase

TagBeschreibung
iscolorhexcolor|rgb|rgba|hsl|hsla
country_codeiso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric

Operatoren

| Tag | Beschreibung | Hex | | --- | -------------------------------------------------------------------- | -------------------------------------------------- | ------ | | , | UND-Operation, verwendet mehrere Validierungs-Tags, alle Bedingungen müssen erfüllt sein, keine Leerzeichen zwischen Kommas | 0x2c | | | | ODER-Operation, verwendet mehrere Validierungs-Tags, aber nur eine muss erfüllt sein | 0x7c | | - | Dieses Feld überspringt die Validierung | 0x2d | | = | Parameter-Zuweisungszeichen | 0x3d |

TIP

Wenn Sie bei der Feldvalidierung Operatoren zuordnen möchten, müssen Sie die UTF-8-Hexadezimaldarstellung verwenden, zum Beispiel:

go
filed string `validate:"contains=0x2c"`

Verwendung

Im Folgenden werden einige grundlegende Verwendungen von Validator sowie einige Code-Beispiele vorgestellt.

Singleton

go
var validate *validator.Validate

Bei der Verwendung empfiehlt der offiziell, dass während des gesamten Programmlebenszyklus nur eine Validator-Instanz existiert, was das Caching von Daten erleichtert.

Validator erstellen

Bei der alleinigen Verwendung von Validator ohne Integration in andere Frameworks müssen wir den Validator manuell erstellen.

go
validate = validator.New()

Struktur-Validierung

go
func (v *Validate) Struct(s interface{}) error

Die Struct-Methode validiert alle öffentlichen Felder einer Struktur. Standardmäßig wird automatisch eine verschachtelte Strukturvalidierung durchgeführt. Wenn ein ungültiger Wert übergeben wird oder der übergebene Wert nil ist, wird InvalidValidationError zurückgegeben. Bei Validierungsfehlern wird ValidationErrors zurückgegeben.

Beispiel

go
package validate

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

type User struct {
  Name    string `validate:"contains=jack"` // Name enthält jack
  Age     int    `validate:"gte=18"`        // größer oder gleich 17 Jahre
  Address string `valiate:"endwith=市"`      // endet mit 市
}

func TestStruct(t *testing.T) {
  validate := validator.New()
  user := User{
    Name:    "jacklove",
    Age:     17,
    Address: "滔博市",
  }
  err := validate.Struct(user)
  for _, err := range err.(validator.ValidationErrors) {
    fmt.Println(err.Namespace()) // Namespace
    fmt.Println(err.Field())
    fmt.Println(err.StructNamespace())
    fmt.Println(err.StructField())
    fmt.Println(err.Tag())
    fmt.Println(err.ActualTag())
    fmt.Println(err.Kind())
    fmt.Println(err.Type())
    fmt.Println(err.Value())
    fmt.Println(err.Param())
    fmt.Println()
  }
  fmt.Println(err)
}

Ausgabe

User.Age
Age
User.Age
Age
gte
gte
int
int
17
18

Key: 'User.Age' Error:Field validation for 'Age' failed on the 'gte' tag

Map-Validierung

go
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{}

Validierung von Schlüssel-Wert-Paaren über ein Tag map.

Beispiel

go
func TestMap(t *testing.T) {
   user := map[string]interface{}{
      "name":    "jak",
      "age":     17,
      "address": "滔博市",
   }
   rules := map[string]interface{}{
      "name":    "contains=jacklove",
      "age":     "gte=18",
      "address": "endswith=市",
   }

   validate := validator.New()

   validateMap := validate.ValidateMap(user, rules)
   fmt.Println(validateMap)
}

Ausgabe

map[age:Key: '' Error:Field validation for '' failed on the 'gte' tag name:Key: '' Error:Field validation for '' failed on the 'contains' tag]

Slice-Validierung

Validierung eines String-Slices. Das Tag vor dive validiert den Slice, das Tag nach dive validiert die Werte im Slice. Verschachtelte Slices funktionieren genauso - für jede Dimension wird ein dive verwendet.

go
func TestSlice1(t *testing.T) {
  list := []string{"jack", "mike", "lisa", "golang"}
  err := validator.New().Var(list, "max=5,dive,contains=a,min=5") // Slice-Länge maximal 5, Element muss Zeichen a enthalten, Mindestlänge 5
  fmt.Println(err)
}

Ausgabe

Key: '[0]' Error:Field validation for '[0]' failed on the 'min' tag
Key: '[1]' Error:Field validation for '[1]' failed on the 'contains' tag
Key: '[2]' Error:Field validation for '[2]' failed on the 'min' tag

Strukturvalidierung für jeden Benutzer im Slice

go
func TestSlice(t *testing.T) {
   userList := make([]User, 0)
   user := User{
      Name:    "jacklove",
      Age:     17,
      Address: "滔博市",
   }
   userList = append(userList, user)
   err := validator.New().Var(userList, "dive") // "dive" bedeutet Tiefenvalidierung, wenn das Element eine Struktur ist, wird automatisch eine Strukturvalidierung durchgeführt
   fmt.Println(err)
}

Ausgabe

Key: '[0].Age' Error:Field validation for 'Age' failed on the 'gte' tag

Variablen-Validierung

Einfach und verständlich, keine weitere Erklärung notwendig.

Beispiel 1

go
func TestVar(t *testing.T) {
   name := "jack"
   err := validator.New().Var(name, "max=5,contains=a,min=1,endswith=l") // Maximale Länge 5, Mindestlänge 1, enthält Buchstabe a, endet mit Buchstabe l
   fmt.Println(err)
}

Ausgabe

Key: '' Error:Field validation for '' failed on the 'endswith' tag

Beispiel 2

func TestVar1(t *testing.T) {
   age := 18
   err := validator.New().Var(age, "gte=19")
   fmt.Println(err)
}

Ausgabe

Key: '' Error:Field validation for '' failed on the 'gte' tag

TIP

Die Var-Methode kann Strukturen, Variablen, Slices und Maps validieren. Verwenden Sie das dive-Tag sinnvoll.

Feld-Validierung

Die Parameter der Feldvalidierung sind keine Basistypen mehr, sondern Strukturfeldnamen. Es kann sich um den eigenen Feldnamen oder um den Feldnamen einer verschachtelten Struktur handeln.

go
type Password struct {
   FirstPassword  string `validate:"eqfield=SecondPassword"` // Validiert, ob zwei eingegebene Passwörter gleich sind
   SecondPassword string
}

type RegisterUser struct {
   Username string `validate:"necsfield=Password.FirstPassword"` // Aus Sicherheitsgründen bei der Registrierung verboten, dass Passwort und Benutzername identisch sind
   Password Password
}

func TestCrossStructFieldValidate(t *testing.T) {
   validate = validator.New()
   // Fehlgeschlagen
   fmt.Println(validate.Struct(RegisterUser{
      Username: "gopher",
      Password: Password{
         FirstPassword:  "gopher",
         SecondPassword: "gophers",
      },
   }))
   // Erfolgreich
   fmt.Println(validate.Struct(RegisterUser{
      Username: "gophers",
      Password: Password{
         FirstPassword:  "gopher",
         SecondPassword: "gopher",
      },
   }))
}

Ausgabe

Key: 'RegisterUser.Username' Error:Field validation for 'Username' failed on the 'necsfield' tag
Key: 'RegisterUser.Password.FirstPassword' Error:Field validation for 'FirstPassword' failed on the 'eqfield' tag
<nil>

WARNING

Bei der Feldvalidierung wird die Validierung direkt als fehlgeschlagen gewertet, wenn das als Tag-Parameter verwendete Feld oder die Struktur nicht existiert, zum Beispiel:

go
type Password struct {
   FirstPassword  string `validate:"eqfield=SeconddPaswod"` // SeconddPaswod != SecondPassword
   SecondPassword string
}

Solche Tippfehler sind schwer zu erkennen, und bei der Validierung wird nur angezeigt, dass die Validierung nicht bestanden wurde. Seien Sie sehr vorsichtig.

Fortgeschritten

Im Folgenden werden einige fortgeschrittene Anwendungstechniken und weitere Anpassungsmöglichkeiten vorgestellt.

Benutzerdefinierte Aliase

Manchmal gibt es für ein Feld sehr viele Validierungs-Tags. Wenn Sie diese für ein anderes Feld wiederverwenden möchten, könnten Sie sie kopieren und einfügen. Das ist jedoch nicht die beste Lösung. Eine bessere Methode ist die Registrierung von Aliasen zur Verbesserung der Wiederverwendbarkeit. Sehen Sie sich das folgende Beispiel an:

go
var validate *validator.Validate

const PERSON_NAME_RULES = "max=10,min=1,contains=jack"

func TestAlias(t *testing.T) {
  validate = validator.New()
    // Alias registrieren
  validate.RegisterAlias("namerules", PERSON_NAME_RULES)
  type person struct {
    FirstName string `validate:"namerules"` // Alias verwenden
    LastName  string `validate:"namerules"`
  }

  err := validate.Struct(person{
    FirstName: "",
    LastName:  "",
  })

  fmt.Println(err)
}

Ausgabe

go
Key: 'person.FirstName' Error:Field validation for 'FirstName' failed on the 'namerules' tag
Key: 'person.LastName' Error:Field validation for 'LastName' failed on the 'namerules' tag

Benutzerdefinierte Validierungsfunktion

Obwohl die mitgelieferten Validierungs-Tags die grundlegenden Anforderungen erfüllen, müssen Sie für einige spezielle Anforderungen möglicherweise Ihre eigene Logik definieren. Validator bietet entsprechende APIs zur Definition von Validierungsfunktionen. Sehen Sie sich zuerst ein Beispiel an:

go
func TestCustomValidate(t *testing.T) {
   validate = validator.New()
   fmt.Println(validate.RegisterValidation("is666", is666))
   type Example struct {
      Name string `validate:"is666"`
   }
   fmt.Println(validate.Struct(Example{Name: "777"}))
   fmt.Println(validate.Struct(Example{Name: "666"}))
}

func is666(fl validator.FieldLevel) bool {
   return fl.Field().String() == "666"
}

Es wurde eine Funktion erstellt, die prüft, ob der Feldwert gleich "666" ist, und das entsprechende Tag ist is666. Die Ausgabe lautet:

go
<nil>
Key: 'Example.Name' Error:Field validation for 'Name' failed on the 'is666' tag

TIP

Wenn das registrierte Tag bereits existiert, wird es überschrieben, d.h. Sie können die Standard-Tag-Validierungslogik "überschreiben".

Benutzerdefinierte Typ-Validierungsfunktion

Typ-Validierungsfunktionen sind speziell für einen bestimmten Typ gedacht, normalerweise für nicht-basale Typen. Ebenso können die Standard-Validierungen für Basistypen überschrieben werden. Hier ein Beispiel:

go
type Address struct {
  name string
}

func TestCustomTypeValidate(t *testing.T) {
  validate = validator.New()
  validate.RegisterCustomTypeFunc(ValidateAddress, Address{}) // Typ-Validierungsfunktion und entsprechenden Typ registrieren
  type Example struct {
    Address Address `validate:"required"`
  }
  fmt.Println(validate.Struct(Example{Address: Address{name: ""}}))
  fmt.Println(validate.Struct(Example{Address: Address{name: "cn"}}))
}

func ValidateAddress(value reflect.Value) interface{} {
  if address, ok := value.Interface().(Address); ok {
    // Fehlerbehandlung
    if address.name == "" {
      return address.name
    }

    return value // Rückgabe des Feldes bedeutet, dass die Validierung korrekt ist
  }
  return nil
}

Ausgabe

go
Key: 'Example.Address' Error:Field validation for 'Address' failed on the 'required' tag
<nil>

TIP

Das Registrieren mehrerer Typen für eine Funktion funktioniert auf die gleiche Weise.

Benutzerdefinierte Struktur-Validierungsfunktion

Der Unterschied bei Struktur-Validierungsfunktionen besteht darin, dass bei anderen Funktionen der Parameter ein Feld ist, während bei dieser Funktion der Parameter eine Struktur ist. Sehen Sie sich das folgende Beispiel an:

go
type People struct {
   FirstName string
   LastName  string
}

func TestCustomStructLevel(t *testing.T) {
   validate = validator.New()
   validate.RegisterStructValidation(PeopleValidate, People{}) // Typ-Registrierung, es können auch mehrere Strukturen übergeben werden
   err := validate.Struct(People{
      FirstName: "",
      LastName:  "",
   })
   fmt.Println(err)
}

func PeopleValidate(sl validator.StructLevel) {
   people := sl.Current().Interface().(People)

   if people.FirstName == "" || people.LastName == "" {
      sl.ReportError(people.FirstName, "FirstName", "FirstName", "", "")
      sl.ReportError(people.FirstName, "LastName", "LastName", "", "")
   }
}

Ausgabe

Key: 'People.FirstName' Error:Field validation for 'FirstName' failed on the '' tag
Key: 'People.LastName' Error:Field validation for 'LastName' failed on the '' tag

Mehrsprachigkeit

Übersetzer-Komponente

go get github.com/go-playground/universal-translator

Gebietsschema-Komponente

go get github.com/go-playground/locales

Die Standardsprache des Validators ist Englisch. Bei der Projektentwicklung können jedoch mehrere Sprachen erforderlich sein. In diesem Fall benötigen wir eine Internationalisierungs-Komponente für mehrere Sprachen. Sehen Sie sich das folgende Beispiel an:

go
import (
  "fmt"
  "github.com/go-playground/locales/zh"
  ut "github.com/go-playground/universal-translator"
  "github.com/go-playground/validator/v10"
  zh_trans "github.com/go-playground/validator/v10/translations/zh"
  "reflect"
  "testing"
)

type User struct {
   Name    string `validate:"contains=jack"` // Name enthält jack
   Age     int    `validate:"gte=18"`        // größer oder gleich 17 Jahre
   Address string `validate:"endswith=市"`    // endet mit 市
}

var (
   uni      *ut.UniversalTranslator
   validate *validator.Validate
)

func TestTranslate(t *testing.T) {
   zh := zh.New()
   // Der erste ist der Ersatz, die folgenden sind unterstützte Sprachen, es können mehrere sein
   uni = ut.New(zh, zh)
   // Die Sprache kann normalerweise aus dem Accept-Language-Header von HTTP-Anfragen abgerufen werden
   trans, found := uni.GetTranslator(zh.Locale())
   validate = validator.New()
   if found {
      zh_trans.RegisterDefaultTranslations(validate, trans) // Standard-Übersetzer registrieren
   }
   err := validate.Struct(User{
      Name:    "",
      Age:     0,
      Address: "",
   })
   fmt.Println(err.(validator.ValidationErrors).Translate(trans))
}

Ausgabe

map[User.Address:Address muss mit Text '市' enden User.Age:Age muss größer oder gleich 18 sein User.Name:Name muss Text 'jack' enthalten]

Sie können auch jeden Fehler einzeln übersetzen:

go
for _, fieldError := range err.(validator.ValidationErrors) {
   fmt.Println(fieldError.Translate(trans))
}

Ausgabe

Name muss Text 'jack' enthalten
Age muss größer oder gleich 18 sein
Address muss mit Text '市' enden

Wie Sie sehen, ist der Rückgabewert eine Map. Die grundlegende Fehlerübersetzung wurde bereits erreicht, reicht aber noch nicht für den praktischen Einsatz. Wir müssen die Fehlermeldung weiter optimieren, um besser mit Kunden oder dem Frontend zu kommunizieren.

go
type User struct {

   Name    string `validate:"contains=jack" label:"姓名"` // Name enthält jack
   Age     int    `validate:"gte=18" label:"年龄"`        // größer oder gleich 17 Jahre
   Address string `validate:"endswith=市" label:"地址"`    // endet mit 市
   Sex     string `validate:"required" label:"性别"`
}

Definieren Sie zunächst ein benutzerdefiniertes Tag label, dessen Wert der chinesische Name des Feldes ist. Registrieren Sie dann beim Validator eine TagNameFunc, die beim Abrufen des Feldnamens den ursprünglichen Namen ersetzt. Im Kommentar zur Field() string-Methode in der Datei errors.go steht: "Feldname mit Tag hat Vorrang vor dem tatsächlichen Feldnamen". Wenn später ein Fehler auftritt, kann der benutzerdefinierte chinesische Name anstelle des englischen Wortes verwendet werden. Die TagNameFunc sieht wie folgt aus:

go
// Wir haben ein benutzerdefiniertes Tag hinzugefügt, das für Strukturfelder den chinesischen Namen angibt und den ursprünglichen Feldnamen ersetzt
func CustomTagNameFunc(field reflect.StructField) string {
   label := field.Tag.Get("label")
   if len(label) == 0 {
      return field.Name
   }
   return label
}

Schließlich registrieren:

go
validate.RegisterTagNameFunc(CustomTagNameFunc)

Erneute Ausführung:

姓名必须包含文本'jack'
年龄必须大于或等于18
地址必须以文本'市'结尾

Das reicht aber noch nicht als Fehlermeldung für das Frontend. Wir müssen die Nachricht in JSON oder ein anderes für die Nachrichtenübertragung geeignetes Format umwandeln. Sie könnten daran denken, die Map direkt in JSON zu serialisieren. Das ist eine Lösung, aber Sie könnten folgendes Ergebnis erhalten:

json
{
  "User.地址": "地址必须以文本'市'结尾",
  "User.姓名": "姓名必须包含文本'back'",
  "User.年龄": "年龄必须大于或等于18",
  "User.性别": "性别为必填字段"
}

Durch Verarbeitung des Map-Schlüssels erhalten Sie folgendes Ergebnis:

json
{
  "地址": "地址必须以文本'市'结尾",
  "姓名": "姓名必须包含文本'back'",
  "年龄": "年龄必须大于或等于18",
  "性别": "性别为必填字段"
}

Es wird jedoch nicht empfohlen, die obige Information an das Frontend zurückzugeben. Wir können sie zu einer Zeichenkette verarbeiten und als Nachricht zurückgeben:

姓名必须包含文本'back', 年龄必须大于或等于18, 地址必须以文本'市'结尾, 性别为必填字段,

Vollständiger Code

go
import (
   "fmt"
   "github.com/go-playground/locales/zh"
   ut "github.com/go-playground/universal-translator"
   "github.com/go-playground/validator/v10"
   zh_trans "github.com/go-playground/validator/v10/translations/zh"
   "reflect"
   "strings"
   "testing"
)

type User struct {
   Name    string `validate:"contains=back" label:"姓名"` // Name enthält jack
   Age     int    `validate:"gte=18" label:"年龄"`        // größer oder gleich 17 Jahre
   Address string `validate:"endswith=市" label:"地址"`    // endet mit 市
   Sex     string `validate:"required" label:"性别"`
}

var (
   uni      *ut.UniversalTranslator
   validate *validator.Validate
)

// Wir haben ein benutzerdefiniertes Tag hinzugefügt, das für Strukturfelder den chinesischen Namen angibt und den ursprünglichen Feldnamen ersetzt
func CustomTagNameFunc(field reflect.StructField) string {
   label := field.Tag.Get("label")
   if len(label) == 0 {
      return field.Name
   }
   return label
}

func TestTranslate(t *testing.T) {
   zh := zh.New()
   uni = ut.New(zh, zh)
   // Die Sprache kann normalerweise aus dem Accept-Language-Header von HTTP-Anfragen abgerufen werden
   trans, found := uni.GetTranslator(zh.Locale())
   validate = validator.New()
   if found {
      zh_trans.RegisterDefaultTranslations(validate, trans) // Standard-Übersetzer registrieren
   }
   validate.RegisterTagNameFunc(CustomTagNameFunc)
   err := validate.Struct(User{
      Name:    "",
      Age:     0,
      Address: "",
   })
   translate := errInfoFormat(err.(validator.ValidationErrors), trans)
   fmt.Println(translate)
}

func errInfoFormat(errors validator.ValidationErrors, trans ut.Translator) string {
   builder := strings.Builder{}
   for _, err := range errors {
      builder.WriteString(err.Translate(trans))
      builder.WriteString(", ")
   }
   return builder.String()
}

Zu guter Letzt: Wenn Sie die Fehlermeldung zu unpersönlich finden und sie menschlicher gestalten möchten, können Sie die Fehlermeldung für ein bestimmtes Tag überschreiben. Dazu benötigen Sie die RegisterTranslation-Methode sowie zwei Funktionstypen: RegisterTranslationsFunc registriert die Übersetzungsvorlage für das entsprechende Tag, und TranslationFunc verarbeitet die Vorlage, um den endgültigen Übersetzungsinhalt zu erhalten. Hier ein Beispiel mit required:

go
func requiredOverrideRegister(ut ut.Translator) error { // Diese Funktion registriert die Übersetzungsvorlage
  return ut.Add("required", "{}是一个必须填写的字段", true) // {} ist ein Platzhalter, true bedeutet, ob eine vorhandene Vorlage überschrieben werden soll
}

func requiredOverrideTranslation(ut ut.Translator, fe validator.FieldError) string { // Diese Funktion ist für den Übersetzungsinhalt zuständig
  t, _ := ut.T("required", fe.Field()) // Es kann mehrere Parameter geben, abhängig davon, wie viele Platzhalter die Vorlage für das entsprechende Tag hat
  return t
}

Schließlich registrieren:

go
validate.RegisterTranslation("required", trans, requiredOverrideRegister, requiredOverrideTranslation)

Ergebnis

姓名必须包含文本'back', 年龄必须大于或等于18, 地址必须以文本'市'结尾, 性别是一个必须填写的字段,

Sprachdateien

Tatsächlich ist das einzelne Registrieren im Code sehr mühsam. universal-translator bietet die Möglichkeit, Übersetzungen über JSON-Konfigurationsdateien zu definieren: universal-translator/examples/full-with-files at master · go-playground/universal-translator (github.com)

go
func TestFilei18n(t *testing.T) {
   validate = validator.New()
   zh := zh.New()
   universalTranslator := ut.New(zh, zh)
   translator, _ := universalTranslator.GetTranslator(zh.Locale())
   zh_trans.RegisterDefaultTranslations(validate, translator)
   er := universalTranslator.Import(ut.FormatJSON, "./zh.json") // Empfohlen, nach der Registrierung zu importieren, um das ursprüngliche Tag zu überschreiben
   if er != nil {
      log.Fatal(er)
   }
   type Gopher struct {
      Language string `validate:"required"`
   }

   err := validate.Struct(Gopher{
      "",
   })
   fmt.Println(err.(validator.ValidationErrors).Translate(translator))
}

JSON-Datei

json
[
  {
    "locale": "zh",
    "key": "required",
    "trans": "这是一个十分重要的字段{0},你必须填写它",
    "override": true
  }
]

Ausgabe

map[Gopher.Language:这是一个十分重要的字段Language,你必须填写它]

TIP

universal-translator hat viele Fallstricke bei der Verwendung. Wenn Sie ein vorhandenes Tag überschreiben möchten, können type und rule leer bleiben, da sie auch in der ursprünglichen Konfiguration nicht ausgefüllt sind. Es ist am besten, konsistent zu bleiben. Welchen type Sie angeben, wird die Konfiguration in die entsprechende Map eingefügt. Wenn Sie Cardinal oder einen anderen type angeben und rule mit one konfiguriert haben, müssen Sie die entsprechende lokale Konfiguration vornehmen, sonst wird ein Fehler gemeldet.

Golang by www.golangdev.cn edit