Validator Doğrulama Kütüphanesi
Dokümantasyon: validator/README.md at master · go-playground/validator (github.com)
Resmi Örnekler: validator/_examples at master · go-playground/validator (github.com)
Giriş
go-playground/validator struct tag tabanlı bir değer doğrulayıcı uygular ve aşağıdaki benzersiz özelliklere sahiptir:
Alanlar arası ve struct arası doğrulama için doğrulama tag'leri ve özel doğrulayıcılar kullanılabilir
Slice'lar diziler haritalar veya herhangi bir çok boyutlu alan doğrulanabilir
Harita anahtarlarını ve değerlerini doğrulamaya dalabilir
Temel türüne göre doğrulamadan önce nasıl işleneceğini belirler
Özel alan türlerini işleyebilir
Alias tag'lerini destekler. Bu birden fazla doğrulamanın tek bir tag'e eşlenmesine olanak tanır ve struct doğrulama tanımını kolaylaştırır
Özel alan adlarını çıkarabilir. Örneğin hata mesajlarında göstermek için JSON adlarını
Çok dilli özel hata mesajları
ginframework'ü için standart varsayılan doğrulama bileşeni
Kurulum
go get github.com/go-playground/validator/v10İçe Aktarma
import "github.com/go-playground/validator/v10"Tag'ler
Validator'ın birçok temel doğrulama tag'i vardır. Tüm tag'e karşılık gelen doğrulama fonksiyonları baked_in.go dosyasında bulunabilir. Validator için struct tag'i validate'dır.
Örneğin:
type User {
age int `validate:"gte=18"` // 18 yaşından büyük veya eşit anlamına gelir
}Ayrıca setTagName metodu ile varsayılan tag'i değiştirebilirsiniz.
Alanlar
| Tag | Açıklama |
|---|---|
eqcsfield | Ayrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alana eşit olduğunu doğrular |
eqfield | Mevcut alanın değerinin param değeri tarafından belirtilen alana eşit olduğunu doğrular |
fieldcontains | Mevcut alanın değerinin param değeri tarafından belirtilen alanı içerdiğini doğrular |
fieldexcludes | Mevcut alanın değerinin param değeri tarafından belirtilen alanı içermediğini doğrular |
gtcsfield | Ayrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alandan büyük olduğunu doğrular |
gtecsfield | Ayrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alandan büyük veya eşit olduğunu doğrular |
gtefield | Mevcut alanın değerinin param değeri tarafından belirtilen alandan büyük veya eşit olduğunu doğrular |
gtfield | Mevcut alanın değerinin param değeri tarafından belirtilen alandan büyük olduğunu doğrular |
ltcsfield | Ayrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alandan küçük olduğunu doğrular |
ltecsfield | Ayrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alandan küçük veya eşit olduğunu doğrular |
ltefield | Mevcut alanın değerinin param değeri tarafından belirtilen alandan küçük veya eşit olduğunu doğrular |
ltfield | Mevcut alanın değerinin param değeri tarafından belirtilen alandan küçük olduğunu doğrular |
necsfield | Mevcut alanın değerinin param değeri tarafından belirtilen ayrı struct'taki alana eşit olmadığını doğrular |
nefield | Mevcut alanın değerinin param değeri tarafından belirtilen alana eşit olmadığını doğrular |
Ağ
| Tag | Açıklama |
|---|---|
cidr | Sınıfsız Alanlar Arası Yönlendirme CIDR |
cidrv4 | Sınıfsız Alanlar Arası Yönlendirme CIDRv4 |
cidrv6 | Sınıfsız Alanlar Arası Yönlendirme CIDRv6 |
datauri | Veri Tekdüzen Kaynak Tanımlayıcısı |
fqdn | Tam Nitelikli Alan Adı (FQDN) |
hostname | Ana bilgisayar adı RFC 952 |
hostname_port | Soket adresleri için yaygın olarak kullanılan <dns>:<port> kombinasyonu için alan doğrulaması |
hostname_rfc1123 | Ana bilgisayar adı RFC 952 |
ip | İnternet Protokolü Adresi IP |
ip4_addr | İnternet Protokolü Adresi IPv4 |
ip6_addr | İnternet Protokolü Adresi IPv6 |
ip_addr | İnternet Protokolü Adresi IP |
ipv4 | İnternet Protokolü Adresi IPv4 |
ipv6 | İnternet Protokolü Adresi IPv6 |
mac | Ortam Erişim Kontrolü adresi. LAN adresi olarak da bilinir |
tcp4_addr | İletim Kontrol Protokolü Adresi TCP4 |
tcp6_addr | İletim Kontrol Protokolü Adresi TCPv6 |
tcp_addr | İletim Kontrol Protokolü Adresi TCP |
udp4_addr | Kullanıcı Datagram Protokolü Adresi UDPv4 |
udp6_addr | Kullanıcı Datagram Protokolü Adresi UDPv6 |
udp_addr | Kullanıcı Datagram Protokolü Adresi UDP |
unix_addr | Unix domain socket uç nokta adresi |
uri | Tekdüzen Kaynak Tanımlayıcısı |
url | Tekdüzen Kaynak Konumlandırıcı |
url_encoded | Tekdüzen Kaynak Tanımlayıcısı kodlaması |
urn_rfc2141 | RFC 2141 Tekdüzen Kaynak Adı |
String'ler
| Tag | Açıklama |
|---|---|
alpha | Mevcut alanın değerinin geçerli bir alfabetik string olduğunu doğrular |
alphanum | Mevcut alanın değerinin geçerli bir alfasayısal string olduğunu doğrular |
alphanumunicode | Mevcut alanın değerinin geçerli bir alfasayısal unicode string olduğunu doğrular |
alphaunicode | Mevcut alanın değerinin geçerli bir alfabetik unicode string olduğunu doğrular |
ascii | Alanın değerinin geçerli ASCII karakterleri olduğunu doğrular |
boolean | Mevcut alanın değerinin geçerli bir boolean olduğunu veya güvenli bir şekilde boolean'a dönüştürülebileceğini doğrular |
contains | Alanın değerinin param'da belirtilen metni içerdiğini doğrular |
containsany | Alanın değerinin param'da belirtilen karakterlerden herhangi birini içerdiğini doğrular |
containsrune | Alanın değerinin param'da belirtilen rune'u içerdiğini doğrular |
endsnotwith | Alanın değerinin param'da belirtilen metin ile bitmediğini doğrular |
endswith | Alanın değerinin param'da belirtilen metin ile bittiğini doğrular |
excludes | Alanın değerinin param'da belirtilen metni içermediğini doğrular |
excludesall | Alanın değerinin param'da belirtilen karakterlerden herhangi birini içermediğini doğrular |
excludesrune | Alanın değerinin param'da belirtilen rune'u içermediğini doğrular |
lowercase | Mevcut alanın değerinin küçük harfli bir string olduğunu doğrular |
multibyte | Alanın değerinin çok baytlı karakterleri olduğunu doğrular |
number | Mevcut alanın değerinin geçerli bir sayı olduğunu doğrular |
numeric | Mevcut alanın değerinin geçerli bir sayısal değer olduğunu doğrular |
printascii | Alanın değerinin geçerli yazdırılabilir ASCII karakterleri olduğunu doğrular |
startsnotwith | Alanın değerinin param'da belirtilen metin ile başlamadığını doğrular |
startswith | Alanın değerinin param'da belirtilen metin ile başladığını doğrular |
uppercase | Mevcut alanın değerinin büyük harfli bir string olduğunu doğrular |
Biçimlendirme
| Tag | Açıklama |
|---|---|
base64 | Base64 string |
base64url | Base64URL string |
bic | Mevcut alanın değerinin ISO 9362'de tanımlandığı gibi geçerli bir BIC kodu (SWIFT kodu) olduğunu doğrular |
bcp47_language_tag | Mevcut alanın değerinin bir BCP47 dil tag'i olduğunu doğrular |
btc_addr | Alanın değerinin geçerli bir BTC adresi olduğunu doğrular |
btc_addr_bech32 | Alanın değerinin geçerli bir bech32 BTC adresi olduğunu doğrular |
credit_card | Mevcut alanın değerinin geçerli bir kredi kartı numarası olduğunu doğrular |
datetime | Mevcut alanın değerinin geçerli bir datetime string'i olduğunu doğrular |
e164 | Mevcut alanın değerinin geçerli bir e.164 formatlı telefon numarası olduğunu doğrular |
email | Mevcut alanın değerinin geçerli bir e-posta adresi olduğunu doğrular |
eth_addr | Alanın değerinin geçerli bir Ethereum adresi olduğunu doğrular |
hexadecimal | Mevcut alanın değerinin geçerli bir onaltılık olduğunu doğrular |
hexcolor | Mevcut alanın değerinin geçerli bir hex renk olduğunu doğrular |
hsl | Mevcut alanın değerinin geçerli bir HSL renk olduğunu doğrular |
hsla | Mevcut alanın değerinin geçerli bir HSLA renk olduğunu doğrular |
html | Mevcut alanın değerinin geçerli HTML olduğunu doğrular |
html_encoded | Mevcut alanın değerinin geçerli HTML kodlanmış olduğunu doğrular |
isbn | Alanın değerinin geçerli bir v10 veya v13 ISBN (Uluslararası Standart Kitap Numarası) olduğunu doğrular |
isbn10 | Alanın değerinin geçerli bir v10 ISBN olduğunu doğrular |
isbn13 | Alanın değerinin geçerli bir v13 ISBN olduğunu doğrular |
iso3166_1_alpha2 | Mevcut alanın değerinin geçerli bir iso3166-1 alpha-2 ülke kodu olduğunu doğrular |
iso3166_1_alpha3 | Mevcut alanın değerinin geçerli bir iso3166-1 alpha-3 ülke kodu olduğunu doğrular |
iso3166_1_alpha_numeric | Mevcut alanın değerinin geçerli bir iso3166-1 alfasayısal ülke kodu olduğunu doğrular |
iso3166_2 | Mevcut alanın değerinin geçerli bir ülke bölge kodu (ISO 3166-2) olduğunu doğrular |
iso4217 | Mevcut alanın değerinin geçerli bir para birimi kodu (ISO 4217) olduğunu doğrular |
json | Mevcut alanın değerinin geçerli bir JSON string'i olduğunu doğrular |
jwt | Mevcut alanın değerinin geçerli bir JWT string'i olduğunu doğrular |
latitude | Alanın değerinin geçerli bir enlem koordinatı olduğunu doğrular |
longitude | Alanın değerinin geçerli bir boylam koordinatı olduğunu doğrular |
postcode_iso3166_alpha2 | Değere göre iso 3166 alpha 2'deki ülke kodu değeri ile doğrular |
postcode_iso3166_alpha2_field | Alana göre doğrular. iso 3166 alpha 2'deki ülke kodu değerini temsil eder |
rgb | Mevcut alanın değerinin geçerli bir RGB renk olduğunu doğrular |
rgba | Mevcut alanın değerinin geçerli bir RGBA renk olduğunu doğrular |
ssn | Alanın değerinin geçerli bir SSN olduğunu doğrular |
timezone | Mevcut alanın değerinin geçerli bir saat dilimi string'i olduğunu doğrular |
uuid | Alanın değerinin herhangi bir versiyonda geçerli bir UUID olduğunu doğrular |
uuid3 | Alanın değerinin geçerli bir UUID v3 olduğunu doğrular |
uuid3_rfc4122 | Alanın değerinin geçerli bir RFC4122 v3 UUID olduğunu doğrular |
uuid4 | Alanın değerinin geçerli bir v4 UUID olduğunu doğrular |
uuid4_rfc4122 | Alanın değerinin geçerli bir RFC4122 v4 UUID olduğunu doğrular |
uuid5 | Alanın değerinin geçerli bir v5 UUID olduğunu doğrular |
uuid5_rfc4122 | Alanın değerinin geçerli bir RFC4122 v5 UUID olduğunu doğrular |
uuid_rfc4122 | Alanın değerinin herhangi bir versiyonda geçerli bir RFC4122 UUID olduğunu doğrular |
md4 | Alanın değerinin geçerli bir MD4 olduğunu doğrular |
md5 | Alanın değerinin geçerli bir MD5 olduğunu doğrular |
sha256 | Alanın değerinin geçerli bir SHA256 olduğunu doğrular |
sha384 | Alanın değerinin geçerli bir SHA384 olduğunu doğrular |
sha512 | Alanın değerinin geçerli bir SHA512 olduğunu doğrular |
ripemd128 | Alanın değerinin geçerli bir RIPEMD128 olduğunu doğrular |
ripemd160 | Alanın değerinin geçerli bir RIPEMD160 olduğunu doğrular |
tiger128 | Alanın değerinin geçerli bir TIGER128 olduğunu doğrular |
tiger160 | Alanın değerinin geçerli bir TIGER160 olduğunu doğrular |
tiger192 | Alanın değerinin geçerli bir TIGER192 olduğunu doğrular |
semver | Mevcut alanın değerinin Semantik Sürümleme 2.0.0'da tanımlandığı gibi geçerli bir semver sürümü olduğunu doğrular |
ulid | Alanın değerinin geçerli bir ULID olduğunu doğrular |
Karşılaştırma
| Tag | Açıklama |
|---|---|
eq | Eşittir |
gt | Büyüktür |
gte | Büyük veya eşittir |
lt | Küçüktür |
lte | Küçük veya eşittir |
ne | Eşit değildir |
Diğer
| Tag | Açıklama |
|---|---|
dir | Dosya dizini |
file | Dosya yolu |
isdefault | Mevcut alanın değerinin varsayılan statik değer olduğunu doğrular |
len | Alan uzunluğu |
max | Maksimum değer |
min | Minimum değer |
oneof | Numaralandırılmış değerlerden biri olup olmadığı |
omitempty | Alan ayarlanmamışsa alanı yoksay |
required | Gerekli değer |
required_if | Diğer tüm belirtilen alanlar belirtilen değere eşit olduğunda alan mevcut olmalı ve boş olmamalıdır |
required_unless | Diğer tüm belirtilen alanlar belirtilen değere eşit olmadığı sürece alan mevcut olmalı ve boş olmamalıdır |
required_with | Belirtilen alanlardan herhangi biri mevcut olduğunda alan mevcut olmalı ve boş olmamalıdır |
required_with_all | Belirtilen alanların tümü mevcut olduğunda alan mevcut olmalı ve boş olmamalıdır |
required_without | Belirtilen alanlardan herhangi biri mevcut olmadığında alan mevcut olmalı ve boş olmamalıdır |
required_without_all | Belirtilen alanların tümü mevcut olmadığında alan mevcut olmalı ve boş olmamalıdır |
excluded_if | Diğer tüm belirtilen alanlar belirtilen değere eşit olduğunda alan yok olabilir veya boş olabilir |
excluded_unless | Diğer tüm belirtilen alanlar belirtilen değere eşit olmadığı sürece alan yok olabilir veya boş olabilir |
excluded_with | Belirtilen alanlardan herhangi biri mevcut olduğunda alan yok olabilir veya boş olabilir |
excluded_with_all | Belirtilen alanların tümü mevcut olduğunda alan yok olabilir veya boş olabilir |
excluded_without | Belirtilen alanlardan herhangi biri mevcut olmadığında alan yok olabilir veya boş olabilir |
excluded_without_all | Belirtilen alanların tümü mevcut olmadığında alan yok olabilir veya boş olabilir |
unique | Her arr, map, slice değerinin benzersiz olduğunu doğrular |
Alias'lar
| Tag | Açıklama |
|---|---|
iscolor | hexcolor|rgb|rgba|hsl|hsla |
country_code | iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric |
Operatörler
| Tag | Açıklama | Hex |
|---|---|---|
, | AND operatörü. Birden fazla doğrulama tag'i kullanın. Tüm koşullar sağlanmalıdır. Virgüller arasında boşluk yoktur | 0x2c |
| | OR operatörü. Birden fazla doğrulama tag'i kullanın ancak yalnızca birinin sağlanması gerekir | 0x7c |
- | Bu alan için doğrulamayı atla | 0x2d |
= | Parametre eşleştirme sembolü | 0x3d |
TIP
Alan doğrulaması sırasında operatörleri eşleştirirken bunları utf8 onaltılık gösterimle değiştirmeniz gerekir. Örneğin:
filed string `validate:"contains=0x2c"`Kullanım
Aşağıda Validator'ın bazı temel kullanımları kod örnekleri ile tanıtılacaktır.
Singleton
var validate *validator.ValidateKullanırken resmi öneri programın yaşam döngüsü boyunca yalnızca bir validator örneği olması yönündedir. Bu bazı verileri önbelleğe almaya yardımcı olacaktır.
Validator Oluştur
Validator'ı diğer framework'lerle entegre etmeden yalnızca kullanırken validator'ü manuel olarak oluşturmamız gerekir.
validate = validator.New()Struct Doğrulama
func (v *Validate) Struct(s interface{}) errorStruct metodu bir struct'ın tüm public alanlarını doğrulamak için kullanılır. Varsayılan olarak otomatik olarak iç içe struct doğrulaması yapar. Geçersiz bir değer geçirildiğinde veya değer nil olduğunda InvalidValidationError döndürür. Doğrulama başarısız olduğunda ValidationErrors döndürür.
Örnek:
package validate
import (
"fmt"
"github.com/go-playground/validator/v10"
"testing"
)
type User struct {
Name string `validate:"contains=jack"` // İsim jack içerir
Age int `validate:"gte=18"` // 18 yaşından büyük veya eşit
Address string `validate:"endswith=市"` // 市 (şehir) ile biter
}
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)
}Çıktı:
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 Doğrulama
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{}Bir map tag'i aracılığıyla anahtar-değer çiftlerini doğrular.
Örnek:
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)
}Çıktı:
map[age:Key: '' Error:Field validation for '' failed on the 'gte' tag name:Key: '' Error:Field validation for '' failed on the 'contains' tag]Slice Doğrulama
String slice'ları doğrular. dive'dan önceki tag'ler slice'ı doğrular. dive'dan sonraki tag'ler slice'taki değerleri doğrular. İç içe slice'lar aynı prensibi takip eder - kaç boyut varsa o kadar dive tag'i kullanın.
func TestSlice1(t *testing.T) {
list := []string{"jack", "mike", "lisa", "golang"}
err := validator.New().Var(list, "max=5,dive,contains=a,min=5") // Slice maksimum uzunluk 5. Elemanlar 'a' karakterini içermelidir ve minimum uzunluk 5'tir
fmt.Println(err)
}Çıktı:
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' tagBir slice'taki her kullanıcıyı struct doğrulaması ile doğrula:
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" derinlemesine doğrulama anlamına gelir. Eleman bir struct olduğunda otomatik olarak struct doğrulaması yapar
fmt.Println(err)
}Çıktı:
Key: '[0].Age' Error:Field validation for 'Age' failed on the 'gte' tagDeğişken Doğrulama
Basit ve anlaşılması kolay. Aşırı açıklama gerektirmez.
Örnek 1:
func TestVar(t *testing.T) {
name := "jack"
err := validator.New().Var(name, "max=5,contains=a,min=1,endswith=l") // Maksimum uzunluk 5, minimum uzunluk 1, 'a' harfini içerir, 'l' harfi ile biter
fmt.Println(err)
}Çıktı:
Key: '' Error:Field validation for '' failed on the 'endswith' tagÖrnek 2:
func TestVar1(t *testing.T) {
age := 18
err := validator.New().Var(age, "gte=19")
fmt.Println(err)
}Çıktı:
Key: '' Error:Field validation for '' failed on the 'gte' tagTIP
Var metodu struct'lar değişkenler slice'lar ve haritalar dahil türleri doğrulayabilir. dive tag'i ile makul bir şekilde birleştirerek kullanın.
Alan Doğrulama
Alan doğrulama parametreleri artık temel türler değil struct alan adlarıdır. Struct'ın kendi alan adları veya iç içe struct alan adları olabilirler.
type Password struct {
FirstPassword string `validate:"eqfield=SecondPassword"` // İki şifre girişinin eşit olup olmadığını doğrular
SecondPassword string
}
type RegisterUser struct {
Username string `validate:"necsfield=Password.FirstPassword"` // Kayıt sırasında güvenlik için şifrenin kullanıcı adıyla eşleşmesini yasakla
Password Password
}
func TestCrossStructFieldValidate(t *testing.T) {
validate = validator.New()
// Başarısız
fmt.Println(validate.Struct(RegisterUser{
Username: "gopher",
Password: Password{
FirstPassword: "gopher",
SecondPassword: "gophers",
},
}))
// Başarılı
fmt.Println(validate.Struct(RegisterUser{
Username: "gophers",
Password: Password{
FirstPassword: "gopher",
SecondPassword: "gopher",
},
}))
}Çıktı:
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
Alan doğrulaması kullanırken Tag parametresi olarak belirtilen alan veya struct mevcut değilse doğrudan doğrulama hatası olarak değerlendirilecektir. Örneğin:
type Password struct {
FirstPassword string `validate:"eqfield=SeconddPaswod"` // SeconddPaswod != SecondPassword
SecondPassword string
}Böyle yazım hatalarını tespit etmek zordur ve doğrulama sırasında yalnızca doğrulama hatası olarak gösterilecektir. Bu özel dikkat gerektirir.
Gelişmiş
Sonraki bazı gelişmiş kullanım tekniklerini ve daha fazla özel işlemi açıklayacağız.
Özel Alias'lar
Bazen bir alanın çok sayıda doğrulama tag'i vardır. Bunları başka bir alanda yeniden kullanmak istediğinizde doğrudan kopyalayıp yapıştırabilirsiniz ancak bu en iyi çözüm değildir. Daha iyi bir yaklaşım yeniden kullanılabilirliği artırmak için alias'ları kaydetmektir. Aşağıdaki örneğe bakın:
var validate *validator.Validate
const PERSON_NAME_RULES = "max=10,min=1,contains=jack"
func TestAlias(t *testing.T) {
validate = validator.New()
// Alias kaydet
validate.RegisterAlias("namerules", PERSON_NAME_RULES)
type person struct {
FirstName string `validate:"namerules"` // Alias kullan
LastName string `validate:"namerules"`
}
err := validate.Struct(person{
FirstName: "",
LastName: "",
})
fmt.Println(err)
}Çıktı:
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Özel Doğrulama Fonksiyonları
Bileşenin yerleşik doğrulama tag'leri temel senaryolar için yeterli olsa da bazen özel gereksinimler için kendi mantığınızı tanımlamanız gerekir. Validator doğrulama fonksiyonlarını özelleştirmek için ilgili API'ler sağlar. Bir örneğe bakalım:
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"
}Alan değerinin "666"'ya eşit olup olmadığını kontrol eden bir fonksiyon oluşturuldu ve buna karşılık gelen tag is666'dır. Çıktı şu şekildedir:
<nil>
Key: 'Example.Name' Error:Field validation for 'Name' failed on the 'is666' tagTIP
Kayıtlı bir tag zaten varsa mevcut olan tarafından üzerine yazılacaktır. Başka bir deyişle varsayılan tag doğrulama mantığını "yeniden yazabilirsiniz".
Özel Tür Doğrulama Fonksiyonları
Tür doğrulama fonksiyonları özellikle belirli türler içindir. Genellikle temel olmayan türler içindir. Benzer şekilde varsayılan temel tür doğrulamasını da geçersiz kılabilirsiniz. Aşağıdaki örneğe bakın:
type Address struct {
name string
}
func TestCustomTypeValidate(t *testing.T) {
validate = validator.New()
validate.RegisterCustomTypeFunc(ValidateAddress, Address{}) // Tür doğrulama fonksiyonunu ve karşılık gelen türü kaydet
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 {
// Hata işleme
if address.name == "" {
return address.name
}
return value // Alanı döndürmek doğrulamanın geçtiği anlamına gelir
}
return nil
}Çıktı:
Key: 'Example.Address' Error:Field validation for 'Address' failed on the 'required' tag
<nil>TIP
Bir fonksiyona birden fazla tür kaydetmek de aynı prensibi takip eder.
Özel Struct Seviyesi Doğrulama Fonksiyonları
Struct seviyesi doğrulama fonksiyonları ile fark diğer fonksiyonların parametrelerinin alanlar olması bu fonksiyonun parametresinin ise struct'ın kendisi olmasıdır. Aşağıdaki örneğe bakın:
type People struct {
FirstName string
LastName string
}
func TestCustomStructLevel(t *testing.T) {
validate = validator.New()
validate.RegisterStructValidation(PeopleValidate, People{}) // Tür kaydı ile aynı. Birden fazla struct türü geçirebilirsiniz
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", "", "")
}
}Çıktı:
Key: 'People.FirstName' Error:Field validation for 'FirstName' failed on the '' tag
Key: 'People.LastName' Error:Field validation for 'LastName' failed on the '' tagÇoklu Dil
Çevirmen bileşeni:
go get github.com/go-playground/universal-translatorYerel ayar bileşeni:
go get github.com/go-playground/localesValidator'ın varsayılan dili İngilizcedir. Projeleri geliştirirken birden fazla dil kullanmamız gerekebilir. Bu durumda uluslararasılaştırma çoklu dil bileşenini kullanmamız gerekir. Aşağıdaki örneğe bakın:
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"` // İsim jack içerir
Age int `validate:"gte=18"` // 18 yaşından büyük veya eşit
Address string `validate:"endswith=市"` // 市 (şehir) ile biter
}
var (
uni *ut.UniversalTranslator
validate *validator.Validate
)
func TestTranslate(t *testing.T) {
zh := zh.New()
// İlki yedek sonraki desteklenen dillerdir. Birden fazla olabilir
uni = ut.New(zh, zh)
// Buradaki dil genellikle HTTP isteklerindeki Accept-Language header'ından alınır
trans, found := uni.GetTranslator(zh.Locale())
validate = validator.New()
if found {
zh_trans.RegisterDefaultTranslations(validate, trans) // Varsayılan çevirmeni kaydet
}
err := validate.Struct(User{
Name: "",
Age: 0,
Address: "",
})
fmt.Println(err.(validator.ValidationErrors).Translate(trans))
}Çıktı:
map[User.Address:Address must end with text '市' User.Age:Age must be greater than or equal to 18 User.Name:Name must contain text 'jack']Her hatayı tek tek de çevirebilirsiniz:
for _, fieldError := range err.(validator.ValidationErrors) {
fmt.Println(fieldError.Translate(trans))
}Çıktı:
Name must contain text 'jack'
Age must be greater than or equal to 18
Address must end with text '市'Dönüş değerinin bir map olduğunu görebilirsiniz. Temel hata mesajı çevirisi sağlandı ancak henüz üretim için hazır değil. Hata mesajlarını müşteriler veya frontend ile daha iyi entegrasyon için daha da güzelleştirmemiz gerekir.
type User struct {
Name string `validate:"contains=jack" label:"姓名"` // İsim jack içerir
Age int `validate:"gte=18" label:"年龄"` // 18 yaşından büyük veya eşit
Address string `validate:"endswith=市" label:"地址"` // 市 (şehir) ile biter
Sex string `validate:"required" label:"性别"`
}İlk olarak label tag'ini özelleştirin. Değeri alanın Çince adıdır. Ardından validator ile bir TagNameFunc kaydedin. Rolü alan adını almak veya orijinal adı değiştirmektir. errors.go dosyasındaki Field() string metodundaki yorum şu şekildedir: "Label tag'i ile alan adı alanın gerçek adından önce gelir". Bu nedenle hatalar oluştuğunda İngilizce kelimeyi değiştirmek için özel Çince adı kullanabiliriz. TagNameFunc şu şekildedir:
// Struct alanlarına orijinal alan adlarını değiştirmek için Çince adlar vermek için kullanılan özel bir tag ekledik
func CustomTagNameFunc(field reflect.StructField) string {
label := field.Tag.Get("label")
if len(label) == 0 {
return field.Name
}
return label
}Sonra kaydedin:
validate.RegisterTagNameFunc(CustomTagNameFunc)Tekrar yürütün. Çıktı:
姓名 must contain text 'jack'
年龄 must be greater than or equal to 18
地址 must end with text '市'Ancak bu henüz yeterli değil. Frontend'e döndürülen hata bilgisi olarak kullanmak için henüz uygun değil. Bilgileri mesaj iletimi için uygun bir JSON veya herhangi bir formata biçimlendirmemiz gerekir. Map'i doğrudan JSON'a serileştirmeyi düşünebilirsiniz. Bu bir çözümdür ancak aşağıdaki sonucu alabilirsiniz:
{
"User.地址": "地址 must end with text '市'",
"User.姓名": "姓名 must contain text 'back'",
"User.年龄": "年龄 must be greater than or equal to 18",
"User.性别": "性别 is required"
}Map'in anahtar değerlerini işleyerek aşağıdaki sonucu alın:
{
"地址": "地址 must end with text '市'",
"姓名": "姓名 must contain text 'back'",
"年龄": "年龄 must be greater than or equal to 18",
"性别": "性别 is required"
}Ancak bu tür bilgileri frontend'e döndürmek önerilmez. Mesaj olarak bir string'e işleyebiliriz:
姓名 must contain text 'back', 年龄 must be greater than or equal to 18, 地址 must end with text '市', 性别 is required,Tam Kod
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:"姓名"` // İsim jack içerir
Age int `validate:"gte=18" label:"年龄"` // 18 yaşından büyük veya eşit
Address string `validate:"endswith=市" label:"地址"` // 市 (şehir) ile biter
Sex string `validate:"required" label:"性别"`
}
var (
uni *ut.UniversalTranslator
validate *validator.Validate
)
// Struct alanlarına orijinal alan adlarını değiştirmek için Çince adlar vermek için kullanılan özel bir tag ekledik
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)
// Buradaki dil genellikle HTTP isteklerindeki Accept-Language header'ından alınır
trans, found := uni.GetTranslator(zh.Locale())
validate = validator.New()
if found {
zh_trans.RegisterDefaultTranslations(validate, trans) // Varsayılan çevirmeni kaydet
}
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()
}Son olarak hata mesajlarının çok soğuk olduğunu düşünüyorsanız ve daha insancıl olmasını istiyorsanız belirli tag'ler için hata mesajlarını yeniden yazabilirsiniz. Bu RegisterTranslation metodunu kullanmayı gerektirir ve ayrıca iki tür fonksiyon gerektirir: RegisterTranslationsFunc karşılık gelen tag için çeviri şablonunu kaydetmekten sorumludur ve TranslationFunc şablonu işleyerek son çeviri içeriğini almaktan sorumludur. İşte required kullanan bir örnek:
func requiredOverrideRegister(ut ut.Translator) error { // Bu fonksiyonun rolü çeviri şablonunu kaydetmektir
return ut.Add("required", "{} is a required field", true) // {} yer tutucudur. true mevcut şablonun üzerine yazılıp yazılmayacağını temsil eder
}
func requiredOverrideTranslation(ut ut.Translator, fe validator.FieldError) string { // Bu fonksiyonun rolü içeriği çevirmektir
t, _ := ut.T("required", fe.Field()) // Parametreler kayıtlı tag şablonunun kaç yer tutucusu olduğuna bağlı olarak birden fazla olabilir
return t
}Son olarak kaydedin:
validate.RegisterTranslation("required", trans, requiredOverrideRegister, requiredOverrideTranslation)Sonuç:
姓名 must contain text 'back', 年龄 must be greater than or equal to 18, 地址 must end with text '市', 性别 is a required field,Dil Dosyaları
Aslında kod ile tek tek kaydetmek çok sıkıcıdır. universal-translator JSON yapılandırma dosyaları aracılığıyla çeviri yapma yolu sağlar: 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") // Kayıttan sonra içe aktarma önerilir. Böylece mevcut tag'lerin üzerine yazabilir
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 dosyası:
[
{
"locale": "zh",
"key": "required",
"trans": "This is a very important field {0}, you must fill it in",
"override": true
}
]Çıktı:
map[Gopher.Language:This is a very important field Language, you must fill it in]TIP
universal-translator kullanırken birçok tuzak vardır. Mevcut Tag'i geçersiz kılmak istiyorsanız type ve rule her ikisi de boş bırakılabilir çünkü orijinal yapılandırmada da yoktur. Tutarlılığı korumak en iyisidir. Doldurduğunuz type ne olursa olsun ilgili haritaya eklenecektir. Cardinal veya diğer type ise ve rule one veya benzeri ile yapılandırılmışsa normal kullanabilmeniz için yerel olarak karşılık gelen yapılandırmaları yapmanız gerekir aksi takdirde hata verecektir.
