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
ginFrameworks
Installation
go get github.com/go-playground/validator/v10Import
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:
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
| Tag | Beschreibung |
|---|---|
eqcsfield | Validiert in einer separaten Struktur, ob der Wert des aktuellen Feldes gleich dem durch den Parameterwert angegebenen Feld ist |
eqfield | Validiert, ob der Wert des aktuellen Feldes gleich dem durch den Parameterwert angegebenen Feld ist |
fieldcontains | Validiert, ob der Wert des aktuellen Feldes das durch den Parameterwert angegebene Feld enthält |
fieldexcludes | Validiert, ob der Wert des aktuellen Feldes das durch den Parameterwert angegebene Feld nicht enthält |
gtcsfield | Validiert in einer separaten Struktur, ob der Wert des aktuellen Feldes größer ist als das durch den Parameterwert angegebene Feld |
gtecsfield | Validiert in einer separaten Struktur, ob der Wert des aktuellen Feldes größer oder gleich dem durch den Parameterwert angegebenen Feld ist |
gtefield | Validiert, ob der Wert des aktuellen Feldes größer oder gleich dem durch den Parameterwert angegebenen Feld ist |
gtfield | Validiert, ob der Wert des aktuellen Feldes größer ist als das durch den Parameterwert angegebene Feld |
ltcsfield | Validiert in einer separaten Struktur, ob der Wert des aktuellen Feldes kleiner ist als das durch den Parameterwert angegebene Feld |
ltecsfield | Validiert in einer separaten Struktur, ob der Wert des aktuellen Feldes kleiner oder gleich dem durch den Parameterwert angegebenen Feld ist |
ltefield | Validiert, ob der Wert des aktuellen Feldes kleiner oder gleich dem durch den Parameterwert angegebenen Feld ist |
ltfield | Validiert, ob der Wert des aktuellen Feldes kleiner ist als das durch den Parameterwert angegebene Feld |
necsfield | Validiert, dass der Wert des aktuellen Feldes nicht gleich dem Feld in der separaten Struktur ist, das durch den Parameterwert angegeben wird |
nefield | Validiert, ob der Wert des aktuellen Feldes ungleich dem durch den Parameterwert angegebenen Feld ist |
Netzwerk
| Tag | Beschreibung |
|---|---|
cidr | Classless Inter-Domain Routing CIDR |
cidrv4 | Classless Inter-Domain Routing CIDRv4 |
cidrv6 | Classless Inter-Domain Routing CIDRv6 |
datauri | Data Uniform Resource Locator |
fqdn | Fully Qualified Domain Name (FQDN) |
hostname | Hostname RFC 952 |
hostname_port | Feldvalidierung für Socket-Adresse<dns>:<port> Kombination |
hostname_rfc1123 | Hostname RFC 952 |
ip | Internet Protocol Address IP |
ip4_addr | Internet Protocol Address IPv4 |
ip6_addr | Internet Protocol Address IPv6 |
ip_addr | Internet Protocol Address IP |
ipv4 | Internet Protocol Address IPv4 |
ipv6 | Internet Protocol Address IPv6 |
mac | Media Access Control Address, auch LAN-Adresse genannt |
tcp4_addr | Transmission Control Protocol Address TCP4 |
tcp6_addr | Transmission Control Protocol Address TCPv6 |
tcp_addr | Transmission Control Protocol Address TCP |
udp4_addr | User Datagram Protocol Address UDPv4 |
udp6_addr | User Datagram Protocol Address UDPv6 |
udp_addr | User Datagram Protocol Address UDP |
unix_addr | Unix Domain Socket Endpunkt-Adresse |
uri | Uniform Resource Identifier |
url | Uniform Resource Locator |
url_encoded | Uniform Resource Identifier Codierung |
urn_rfc2141 | RFC 2141 Uniform Resource Name |
Zeichenfolgen
| Tag | Beschreibung |
|---|---|
alpha | Validiert, ob der Wert des aktuellen Feldes gültige Buchstaben sind |
alphanum | Validiert, ob der Wert des aktuellen Feldes gültige alphanumerische Zeichen sind |
alphanumunicode | Validiert, ob der Wert des aktuellen Feldes gültige alphanumerische Unicode-Zeichen ist |
alphaunicode | Validiert, ob der Wert des aktuellen Feldes gültige Unicode-Buchstaben ist |
ascii | Validiert, ob der Wert des Feldes ein gültiges ASCII-Zeichen ist |
boolean | Validiert, ob der Wert des aktuellen Feldes ein gültiger boolescher Wert ist oder sicher in einen booleschen Wert konvertiert werden kann |
contains | Validiert, ob der Wert des Feldes den im Parameter angegebenen Text enthält |
containsany | Validiert, ob der Wert des Feldes eines der im Parameter angegebenen Zeichen enthält |
containsrune | Validiert, ob der Wert des Feldes das im Parameter angegebene Rune enthält |
endsnotwith | Validiert, dass der Wert des Feldes nicht mit dem im Parameter angegebenen Text endet |
endswith | Validiert, dass der Wert des Feldes mit dem im Parameter angegebenen Text endet |
excludes | Validiert, dass der Wert des Feldes den im Parameter angegebenen Text nicht enthält |
excludesall | Validiert, dass der Wert des Feldes keines der im Parameter angegebenen Zeichen enthält |
excludesrune | Validiert, dass der Wert des Feldes das im Parameter angegebene Zeichen nicht enthält |
lowercase | Validiert, ob der Wert des aktuellen Feldes eine Kleinbuchstaben-Zeichenkette ist |
multibyte | Validiert, ob der Wert des Feldes Mehrbyte-Zeichen hat |
number | Validiert, ob der Wert des aktuellen Feldes eine gültige Zahl ist |
numeric | Validiert, ob der Wert des aktuellen Feldes ein gültiger numerischer Wert ist |
printascii | Validiert, ob der Wert des Feldes ein gültiges druckbares ASCII-Zeichen ist |
startsnotwith | Validiert, dass der Wert des Feldes nicht mit dem im Parameter angegebenen Text beginnt |
startswith | Validiert, ob der Wert des Feldes mit dem im Parameter angegebenen Text beginnt |
uppercase | Validiert, ob der Wert des aktuellen Feldes eine Großbuchstaben-Zeichenkette ist |
Formatierung
| Tag | Beschreibung |
|---|---|
base64 | Base64-Zeichenkette |
base64url | Base64URL-Zeichenkette |
bic | Validiert, ob der Wert des aktuellen Feldes ein gültiger BIC-Code (SWIFT-Code) nach ISO 9362 ist |
bcp47_language_tag | Validiert, ob der Wert des aktuellen Feldes ein Sprach-Tag nach BCP47-Spezifikation ist |
btc_addr | Validiert, ob der Wert des Feldes eine gültige BTC-Adresse ist |
btc_addr_bech32 | Validiert, ob der Wert des Feldes eine gültige bech32 BTC-Adresse ist |
credit_card | Validiert, ob der Wert des aktuellen Feldes eine gültige Kreditkartennummer ist |
datetime | Validiert, ob der Wert des aktuellen Feldes eine gültige Datums-Zeichenkette ist |
e164 | Validiert, ob der Wert des aktuellen Feldes eine gültige Telefonnummer im E.164-Format ist |
email | Validiert, ob der Wert des aktuellen Feldes eine gültige E-Mail-Adresse ist |
eth_addr | Validiert, ob der Wert des Feldes eine gültige Ethereum-Adresse ist |
hexadecimal | Validiert, ob der Wert des aktuellen Feldes gültig hexadezimal ist |
hexcolor | Validiert, ob der Wert des aktuellen Feldes eine gültige Hexadezimal-Farbe ist |
hsl | Validiert, ob der Wert des aktuellen Feldes eine gültige HSL-Farbe ist |
hsla | Validiert, ob der Wert des aktuellen Feldes eine gültige HSLA-Farbe ist |
html | Validiert, ob der Wert des aktuellen Feldes gültiges HTML ist |
html_encoded | Validiert, ob der Wert des aktuellen Feldes gültiges HTML-codiert ist |
isbn | Validiert, ob der Wert des Feldes eine gültige v10 oder v13 ISBN (Internationale Standardbuchnummer) ist |
isbn10 | Validiert, ob der Wert des Feldes eine gültige v10 ISBN (Internationale Standardbuchnummer) ist |
isbn13 | Validiert, ob der Wert des Feldes eine gültige v13 ISBN (Internationale Standardbuchnummer) ist |
iso3166_1_alpha2 | Validiert, ob der Wert des aktuellen Feldes ein gültiger iso3166-1 Alpha-2 Ländercode ist |
iso3166_1_alpha3 | Validiert, ob der Wert des aktuellen Feldes ein gültiger iso3166-1 Alpha-3 Ländercode ist |
iso3166_1_alpha_numeric | Validiert, ob der Wert des aktuellen Feldes ein gültiger iso3166-1 alphanumerischer Ländercode ist |
iso3166_2 | Validiert, ob der Wert des aktuellen Feldes ein gültischer Länderregionscode (ISO 3166-2) ist |
iso4217 | Validiert, ob der Wert des aktuellen Feldes ein gültiger Währungscode (ISO 4217) ist |
json | Validiert, ob der Wert des aktuellen Feldes eine gültige JSON-Zeichenkette ist |
jwt | Validiert, ob der Wert des aktuellen Feldes eine gültige JWT-Zeichenkette ist |
latitude | Validiert, ob der Wert des Feldes gültige Breitenkoordinaten ist |
longitude | Validiert, ob der Wert des Feldes gültige Längenkoordinaten ist |
postcode_iso3166_alpha2 | Validiert basierend auf dem Ländercode-Wert in ISO 3166 Alpha 2 |
postcode_iso3166_alpha2_field | Validiert durch ein Feld, das den Ländercode-Wert in ISO 3166 Alpha 2 darstellt |
rgb | Validiert, ob der Wert des aktuellen Feldes eine gültige RGB-Farbe ist |
rgba | Validiert, ob der Wert des aktuellen Feldes eine gültige RGBA-Farbe ist |
ssn | Validiert, ob der Wert des Feldes eine gültige SSN ist |
timezone | Validiert, ob der Wert des aktuellen Feldes eine gültige Zeitzonen-Zeichenkette ist |
uuid | Validiert, ob der Wert des Feldes eine gültige UUID einer beliebigen Version ist |
uuid3 | Validiert, ob der Wert des Feldes eine gültige UUID v3 ist |
uuid3_rfc4122 | Validiert, ob der Wert des Feldes eine gültige RFC4122 v3 UUID ist |
uuid4 | Validiert, ob der Wert des Feldes eine gültige v4 UUID ist |
uuid4_rfc4122 | Validiert, ob der Wert des Feldes eine gültige RFC4122 v4 UUID ist |
uuid5 | Validiert, ob der Wert des Feldes eine gültige v5 UUID ist |
uuid5_rfc4122 | Validiert, ob der Wert des Feldes eine gültige RFC4122 v5 UUID ist |
uuid_rfc4122 | Validiert, ob der Wert des Feldes eine gültige RFC4122 UUID einer beliebigen Version ist |
md4 | Validiert, ob der Wert des Feldes ein gültiges MD4 ist |
md5 | Validiert, ob der Wert des Feldes ein gültiges MD5 ist |
sha256 | Validiert, ob der Wert des Feldes ein gültiges SHA256 ist |
sha384 | Validiert, ob der Wert des Feldes ein gültiges SHA384 ist |
sha512 | Validiert, ob der Wert des Feldes ein gültiges SHA512 ist |
ripemd128 | Validiert, ob der Wert des Feldes ein gültiges PIPEMD128 ist |
ripemd128 | Validiert, ob der Wert des Feldes ein gültiges PIPEMD160 ist |
tiger128 | Validiert, ob der Wert des Feldes ein gültiges TIGER128 ist |
tiger160 | Validiert, ob der Wert des Feldes ein gültiges TIGER160 ist |
tiger192 | Validiert, ob der Wert des Feldes ein gültiges TIGER192 ist |
semver | Validiert, ob der Wert des aktuellen Feldes eine gültige Semver-Version nach Semantic Versioning 2.0.0 ist |
ulid | Validiert, ob der Wert des Feldes eine gültige ULID ist |
Vergleich
| Tag | Beschreibung |
|---|---|
eq | Gleich |
gt | Größer |
gte | Größer gleich |
lt | Kleiner |
lte | Kleiner gleich |
ne | Ungleich |
Sonstige
| Tag | Beschreibung |
|---|---|
dir | Verzeichnis |
file | Dateipfad |
isdefault | Validiert, ob der Wert des aktuellen Feldes der Standard-Statikwert ist |
len | Feldlänge |
max | Maximalwert |
min | Minimalwert |
oneof | Ob einer der aufgelisteten Werte |
oimtempty | Wenn das Feld nicht gesetzt ist, wird das Feld ignoriert |
required | Pflichtwert |
required_if | Nur wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, muss das Validierungsfeld existieren und darf nicht leer sein |
required_unless | Außer wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, muss das Validierungsfeld existieren und darf nicht leer sein |
required_with | Wenn eines der angegebenen Felder existiert, muss das Validierungsfeld existieren und darf nicht leer sein |
required_with_all | Wenn alle angegebenen Felder existieren, muss das Validierungsfeld existieren und darf nicht leer sein |
required_without | Wenn eines der angegebenen Felder nicht existiert, muss das Validierungsfeld existieren und darf nicht leer sein |
required_without_all | Wenn keines der angegebenen Felder existiert, muss das Validierungsfeld existieren und darf nicht leer sein |
excluded_if | Nur wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, darf das Validierungsfeld nicht existieren oder leer sein |
excluded_unless | Außer wenn alle anderen angegebenen Felder gleich dem angegebenen Wert sind, darf das Validierungsfeld nicht existieren oder leer sein |
excluded_with | Wenn eines der angegebenen Felder existiert, darf das Validierungsfeld nicht existieren oder leer sein |
excluded_with_all | Wenn alle angegebenen Felder existieren, darf das Validierungsfeld nicht existieren oder leer sein |
excluded_without | Wenn eines der angegebenen Felder nicht existiert, darf das Validierungsfeld nicht existieren oder leer sein |
excluded_without_all | Wenn keines der angegebenen Felder existiert, darf das Validierungsfeld nicht existieren oder leer sein |
unique | Validiert, ob jeder arr-, map-, slice-Wert eindeutig ist |
Aliase
| Tag | Beschreibung |
|---|---|
iscolor | hexcolor|rgb|rgba|hsl|hsla |
country_code | iso3166_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:
filed string `validate:"contains=0x2c"`Verwendung
Im Folgenden werden einige grundlegende Verwendungen von Validator sowie einige Code-Beispiele vorgestellt.
Singleton
var validate *validator.ValidateBei 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.
validate = validator.New()Struktur-Validierung
func (v *Validate) Struct(s interface{}) errorDie 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
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' tagMap-Validierung
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
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.
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' tagStrukturvalidierung für jeden Benutzer im Slice
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' tagVariablen-Validierung
Einfach und verständlich, keine weitere Erklärung notwendig.
Beispiel 1
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' tagBeispiel 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' tagTIP
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.
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:
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:
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
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' tagBenutzerdefinierte 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:
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:
<nil>
Key: 'Example.Name' Error:Field validation for 'Name' failed on the 'is666' tagTIP
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:
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
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:
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 '' tagMehrsprachigkeit
Übersetzer-Komponente
go get github.com/go-playground/universal-translatorGebietsschema-Komponente
go get github.com/go-playground/localesDie 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:
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:
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 '市' endenWie 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.
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:
// 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:
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:
{
"User.地址": "地址必须以文本'市'结尾",
"User.姓名": "姓名必须包含文本'back'",
"User.年龄": "年龄必须大于或等于18",
"User.性别": "性别为必填字段"
}Durch Verarbeitung des Map-Schlüssels erhalten Sie folgendes Ergebnis:
{
"地址": "地址必须以文本'市'结尾",
"姓名": "姓名必须包含文本'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
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:
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:
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)
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
[
{
"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.
