Skip to content

Validator Doğrulama Kütüphanesi

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

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

Resmi Örnekler: validator/_examples at master · go-playground/validator (github.com)

Kıyaslamalar: go-playground/validator: 💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving (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ı

  • gin framework'ü için standart varsayılan doğrulama bileşeni

Kurulum

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

İçe Aktarma

go
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:

go
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

TagAçıklama
eqcsfieldAyrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alana eşit olduğunu doğrular
eqfieldMevcut alanın değerinin param değeri tarafından belirtilen alana eşit olduğunu doğrular
fieldcontainsMevcut alanın değerinin param değeri tarafından belirtilen alanı içerdiğini doğrular
fieldexcludesMevcut alanın değerinin param değeri tarafından belirtilen alanı içermediğini doğrular
gtcsfieldAyrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alandan büyük olduğunu doğrular
gtecsfieldAyrı 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
gtefieldMevcut alanın değerinin param değeri tarafından belirtilen alandan büyük veya eşit olduğunu doğrular
gtfieldMevcut alanın değerinin param değeri tarafından belirtilen alandan büyük olduğunu doğrular
ltcsfieldAyrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alandan küçük olduğunu doğrular
ltecsfieldAyrı bir struct'ta mevcut alanın değerinin param değeri tarafından belirtilen alandan küçük veya eşit olduğunu doğrular
ltefieldMevcut alanın değerinin param değeri tarafından belirtilen alandan küçük veya eşit olduğunu doğrular
ltfieldMevcut alanın değerinin param değeri tarafından belirtilen alandan küçük olduğunu doğrular
necsfieldMevcut alanın değerinin param değeri tarafından belirtilen ayrı struct'taki alana eşit olmadığını doğrular
nefieldMevcut alanın değerinin param değeri tarafından belirtilen alana eşit olmadığını doğrular

TagAçıklama
cidrSınıfsız Alanlar Arası Yönlendirme CIDR
cidrv4Sınıfsız Alanlar Arası Yönlendirme CIDRv4
cidrv6Sınıfsız Alanlar Arası Yönlendirme CIDRv6
datauriVeri Tekdüzen Kaynak Tanımlayıcısı
fqdnTam Nitelikli Alan Adı (FQDN)
hostnameAna bilgisayar adı RFC 952
hostname_portSoket adresleri için yaygın olarak kullanılan <dns>:<port> kombinasyonu için alan doğrulaması
hostname_rfc1123Ana 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
macOrtam 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_addrKullanıcı Datagram Protokolü Adresi UDPv4
udp6_addrKullanıcı Datagram Protokolü Adresi UDPv6
udp_addrKullanıcı Datagram Protokolü Adresi UDP
unix_addrUnix domain socket uç nokta adresi
uriTekdüzen Kaynak Tanımlayıcısı
urlTekdüzen Kaynak Konumlandırıcı
url_encodedTekdüzen Kaynak Tanımlayıcısı kodlaması
urn_rfc2141RFC 2141 Tekdüzen Kaynak Adı

String'ler

TagAçıklama
alphaMevcut alanın değerinin geçerli bir alfabetik string olduğunu doğrular
alphanumMevcut alanın değerinin geçerli bir alfasayısal string olduğunu doğrular
alphanumunicodeMevcut alanın değerinin geçerli bir alfasayısal unicode string olduğunu doğrular
alphaunicodeMevcut alanın değerinin geçerli bir alfabetik unicode string olduğunu doğrular
asciiAlanın değerinin geçerli ASCII karakterleri olduğunu doğrular
booleanMevcut 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
containsAlanın değerinin param'da belirtilen metni içerdiğini doğrular
containsanyAlanın değerinin param'da belirtilen karakterlerden herhangi birini içerdiğini doğrular
containsruneAlanın değerinin param'da belirtilen rune'u içerdiğini doğrular
endsnotwithAlanın değerinin param'da belirtilen metin ile bitmediğini doğrular
endswithAlanın değerinin param'da belirtilen metin ile bittiğini doğrular
excludesAlanın değerinin param'da belirtilen metni içermediğini doğrular
excludesallAlanın değerinin param'da belirtilen karakterlerden herhangi birini içermediğini doğrular
excludesruneAlanın değerinin param'da belirtilen rune'u içermediğini doğrular
lowercaseMevcut alanın değerinin küçük harfli bir string olduğunu doğrular
multibyteAlanın değerinin çok baytlı karakterleri olduğunu doğrular
numberMevcut alanın değerinin geçerli bir sayı olduğunu doğrular
numericMevcut alanın değerinin geçerli bir sayısal değer olduğunu doğrular
printasciiAlanın değerinin geçerli yazdırılabilir ASCII karakterleri olduğunu doğrular
startsnotwithAlanın değerinin param'da belirtilen metin ile başlamadığını doğrular
startswithAlanın değerinin param'da belirtilen metin ile başladığını doğrular
uppercaseMevcut alanın değerinin büyük harfli bir string olduğunu doğrular

Biçimlendirme

TagAçıklama
base64Base64 string
base64urlBase64URL string
bicMevcut alanın değerinin ISO 9362'de tanımlandığı gibi geçerli bir BIC kodu (SWIFT kodu) olduğunu doğrular
bcp47_language_tagMevcut alanın değerinin bir BCP47 dil tag'i olduğunu doğrular
btc_addrAlanın değerinin geçerli bir BTC adresi olduğunu doğrular
btc_addr_bech32Alanın değerinin geçerli bir bech32 BTC adresi olduğunu doğrular
credit_cardMevcut alanın değerinin geçerli bir kredi kartı numarası olduğunu doğrular
datetimeMevcut alanın değerinin geçerli bir datetime string'i olduğunu doğrular
e164Mevcut alanın değerinin geçerli bir e.164 formatlı telefon numarası olduğunu doğrular
emailMevcut alanın değerinin geçerli bir e-posta adresi olduğunu doğrular
eth_addrAlanın değerinin geçerli bir Ethereum adresi olduğunu doğrular
hexadecimalMevcut alanın değerinin geçerli bir onaltılık olduğunu doğrular
hexcolorMevcut alanın değerinin geçerli bir hex renk olduğunu doğrular
hslMevcut alanın değerinin geçerli bir HSL renk olduğunu doğrular
hslaMevcut alanın değerinin geçerli bir HSLA renk olduğunu doğrular
htmlMevcut alanın değerinin geçerli HTML olduğunu doğrular
html_encodedMevcut alanın değerinin geçerli HTML kodlanmış olduğunu doğrular
isbnAlanın değerinin geçerli bir v10 veya v13 ISBN (Uluslararası Standart Kitap Numarası) olduğunu doğrular
isbn10Alanın değerinin geçerli bir v10 ISBN olduğunu doğrular
isbn13Alanın değerinin geçerli bir v13 ISBN olduğunu doğrular
iso3166_1_alpha2Mevcut alanın değerinin geçerli bir iso3166-1 alpha-2 ülke kodu olduğunu doğrular
iso3166_1_alpha3Mevcut alanın değerinin geçerli bir iso3166-1 alpha-3 ülke kodu olduğunu doğrular
iso3166_1_alpha_numericMevcut alanın değerinin geçerli bir iso3166-1 alfasayısal ülke kodu olduğunu doğrular
iso3166_2Mevcut alanın değerinin geçerli bir ülke bölge kodu (ISO 3166-2) olduğunu doğrular
iso4217Mevcut alanın değerinin geçerli bir para birimi kodu (ISO 4217) olduğunu doğrular
jsonMevcut alanın değerinin geçerli bir JSON string'i olduğunu doğrular
jwtMevcut alanın değerinin geçerli bir JWT string'i olduğunu doğrular
latitudeAlanın değerinin geçerli bir enlem koordinatı olduğunu doğrular
longitudeAlanın değerinin geçerli bir boylam koordinatı olduğunu doğrular
postcode_iso3166_alpha2Değere göre iso 3166 alpha 2'deki ülke kodu değeri ile doğrular
postcode_iso3166_alpha2_fieldAlana göre doğrular. iso 3166 alpha 2'deki ülke kodu değerini temsil eder
rgbMevcut alanın değerinin geçerli bir RGB renk olduğunu doğrular
rgbaMevcut alanın değerinin geçerli bir RGBA renk olduğunu doğrular
ssnAlanın değerinin geçerli bir SSN olduğunu doğrular
timezoneMevcut alanın değerinin geçerli bir saat dilimi string'i olduğunu doğrular
uuidAlanın değerinin herhangi bir versiyonda geçerli bir UUID olduğunu doğrular
uuid3Alanın değerinin geçerli bir UUID v3 olduğunu doğrular
uuid3_rfc4122Alanın değerinin geçerli bir RFC4122 v3 UUID olduğunu doğrular
uuid4Alanın değerinin geçerli bir v4 UUID olduğunu doğrular
uuid4_rfc4122Alanın değerinin geçerli bir RFC4122 v4 UUID olduğunu doğrular
uuid5Alanın değerinin geçerli bir v5 UUID olduğunu doğrular
uuid5_rfc4122Alanın değerinin geçerli bir RFC4122 v5 UUID olduğunu doğrular
uuid_rfc4122Alanın değerinin herhangi bir versiyonda geçerli bir RFC4122 UUID olduğunu doğrular
md4Alanın değerinin geçerli bir MD4 olduğunu doğrular
md5Alanın değerinin geçerli bir MD5 olduğunu doğrular
sha256Alanın değerinin geçerli bir SHA256 olduğunu doğrular
sha384Alanın değerinin geçerli bir SHA384 olduğunu doğrular
sha512Alanın değerinin geçerli bir SHA512 olduğunu doğrular
ripemd128Alanın değerinin geçerli bir RIPEMD128 olduğunu doğrular
ripemd160Alanın değerinin geçerli bir RIPEMD160 olduğunu doğrular
tiger128Alanın değerinin geçerli bir TIGER128 olduğunu doğrular
tiger160Alanın değerinin geçerli bir TIGER160 olduğunu doğrular
tiger192Alanın değerinin geçerli bir TIGER192 olduğunu doğrular
semverMevcut 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
ulidAlanın değerinin geçerli bir ULID olduğunu doğrular

Karşılaştırma

TagAçıklama
eqEşittir
gtBüyüktür
gteBüyük veya eşittir
ltKüçüktür
lteKüçük veya eşittir
neEşit değildir

Diğer

TagAçıklama
dirDosya dizini
fileDosya yolu
isdefaultMevcut alanın değerinin varsayılan statik değer olduğunu doğrular
lenAlan uzunluğu
maxMaksimum değer
minMinimum değer
oneofNumaralandırılmış değerlerden biri olup olmadığı
omitemptyAlan ayarlanmamışsa alanı yoksay
requiredGerekli değer
required_ifDiğer tüm belirtilen alanlar belirtilen değere eşit olduğunda alan mevcut olmalı ve boş olmamalıdır
required_unlessDiğer tüm belirtilen alanlar belirtilen değere eşit olmadığı sürece alan mevcut olmalı ve boş olmamalıdır
required_withBelirtilen alanlardan herhangi biri mevcut olduğunda alan mevcut olmalı ve boş olmamalıdır
required_with_allBelirtilen alanların tümü mevcut olduğunda alan mevcut olmalı ve boş olmamalıdır
required_withoutBelirtilen alanlardan herhangi biri mevcut olmadığında alan mevcut olmalı ve boş olmamalıdır
required_without_allBelirtilen alanların tümü mevcut olmadığında alan mevcut olmalı ve boş olmamalıdır
excluded_ifDiğer tüm belirtilen alanlar belirtilen değere eşit olduğunda alan yok olabilir veya boş olabilir
excluded_unlessDiğer tüm belirtilen alanlar belirtilen değere eşit olmadığı sürece alan yok olabilir veya boş olabilir
excluded_withBelirtilen alanlardan herhangi biri mevcut olduğunda alan yok olabilir veya boş olabilir
excluded_with_allBelirtilen alanların tümü mevcut olduğunda alan yok olabilir veya boş olabilir
excluded_withoutBelirtilen alanlardan herhangi biri mevcut olmadığında alan yok olabilir veya boş olabilir
excluded_without_allBelirtilen alanların tümü mevcut olmadığında alan yok olabilir veya boş olabilir
uniqueHer arr, map, slice değerinin benzersiz olduğunu doğrular

Alias'lar

TagAçıklama
iscolorhexcolor|rgb|rgba|hsl|hsla
country_codeiso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric

Operatörler

TagAçıklamaHex
,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 yoktur0x2c
|OR operatörü. Birden fazla doğrulama tag'i kullanın ancak yalnızca birinin sağlanması gerekir0x7c
-Bu alan için doğrulamayı atla0x2d
=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:

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

Kullanım

Aşağıda Validator'ın bazı temel kullanımları kod örnekleri ile tanıtılacaktır.

Singleton

go
var validate *validator.Validate

Kullanı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.

go
validate = validator.New()

Struct Doğrulama

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

Struct 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:

go
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' tag

Map Doğrulama

go
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:

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)
}

Çı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.

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 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' tag

Bir slice'taki her kullanıcıyı struct doğrulaması ile doğrula:

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" 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' tag

Değişken Doğrulama

Basit ve anlaşılması kolay. Aşırı açıklama gerektirmez.

Örnek 1:

go
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:

go
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' tag

TIP

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.

go
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:

go
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:

go
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ı:

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

Ö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:

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"
}

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:

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

TIP

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:

go
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ı:

go
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:

go
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-translator

Yerel ayar bileşeni:

go get github.com/go-playground/locales

Validator'ı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:

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"` // İ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:

go
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.

go
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:

go
// 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:

go
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:

json
{
  "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:

json
{
  "地址": "地址 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

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:"姓名"` // İ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:

go
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:

go
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)

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") // 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ı:

json
[
  {
    "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.

Golang by www.golangdev.cn edit