The golang data verifies the implementation of validator
- 2020-11-20 06:08:18
- OfStack
preface
In THE application of web, we often encounter data validation problems, and the common validation methods are quite tedious. Here is a package validator that is used more.
The principle of
Write the validation rule in struct pair field tag, and then get tag of struct through reflection (reflect) to realize data validation.
The installation
go get github.com/go-playground/validator/v10
The sample
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Users struct {
Phone string `form:"phone" json:"phone" validate:"required"`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Phone: "1326654487",
Passwd: "123",
Code: "123456",
}
validate := validator.New()
err := validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err)//Key: 'Users.Passwd' Error:Field validation for 'Passwd' failed on the 'min' tag
return
}
}
return
}
The validation rules
required: required email: Verify that the string is email format; Example: "email" url: This will verify that the string value contains a valid URL; Example: "url" max: Maximum length of string; Example: "max = 20" min: Minimum length of string; Example: "min = 6" excludesall: Cannot contain special characters; Example: "excludesall=0x2C" // Note that this is expressed in base 106. len: The character length must be equal to n, or the len value of array, slice, map is n, that is, the number of items contained; Example: "len = 6" eq: The number is equal to n, or the len value of array, slice, map is n, that is, the number of items contained; Example: "eq = 6" ne: the number is not equal to n, or the len value of array, slice, map is not equal to n, that is, the number of items is not n, which is opposite to eq; Example: "ne = 6" gt: the number is greater than n, or the len value of array, slice, map is greater than n, that is, the number of items contained is greater than n; Example: "gt = 6" gte: the number is greater than or equal to n, or the len value of array, slice, map is greater than or equal to n, i.e. the number of items contained is greater than or equal to n; Example: "gte = 6" lt: the number is less than n, or the len value of array, slice and map is less than n, that is, the number of items contained is less than n; Example: "lt = 6" lte: the number is less than or equal to n, or the len value of array, slice, map is less than or equal to n, i.e. the number of items contained is less than or equal to n; Example: "lte = 6"Cross-field validation
If you want to implement a similar scenario, such as comparing the input password and confirm whether the password is 1
eqfield=Field: must equal the value of Field; nefield=Field: must not be equal to the value of Field; gtfield=Field: must be greater than Field; gtefield=Field: must be greater than or equal to Field; ltfield=Field: Must be less than the value of Field; ltefield=Field: must be less than or equal to Field; eqcsfield= ES110en.ES111en: must be equal to the value of Field in struct Other; necsfield=Other.Field: must not equal the value of Field in struct Other; gtcsfield= ES122en. Field: Must be greater than the value of struct Other; gtecsfield=Other.Field: must be greater than or equal to the value of Field in struct Other; ltcsfield= ES134en. Field: Must be less than the value of Field in struct Other; ltecsfield=Other.Field: must be less than or equal to the value of Field in struct Other;
The sample
type UserReg struct {
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Repasswd string `form:"repasswd" json:"repasswd" validate:"required,max=20,min=6,eqfield=Passwd"`
}
The example verifies that the Passwd, Repasswd values are equal. If you would like to understand more types, please reference documentation https: / / godoc org/gopkg in/go - playground/validator. v10
Custom validation types
Example:
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Users struct {
Name string `form:"name" json:"name" validate:"required,CustomValidationErrors"`// Contains custom functions
Age uint8 `form:"age" json:"age" validate:"required,gt=18"`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Name: "admin",
Age: 12,
Passwd: "123",
Code: "123456",
}
validate := validator.New()
// Register custom functions
_=validate.RegisterValidation("CustomValidationErrors", CustomValidationErrors)
err := validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err)//Key: 'Users.Name' Error:Field validation for 'Name' failed on the 'CustomValidationErrors' tag
return
}
}
return
}
func CustomValidationErrors(fl validator.FieldLevel) bool {
return fl.Field().String() != "admin"
}
Translation error message in Chinese
As you can see from the above example, the default error message for validator is similar to the following
Key: 'Users.Name' Error:Field validation for 'Name' failed on the 'CustomValidationErrors' tag
Obviously that's not what we want to do if we want to translate it into Chinese or any other language? go-playground provides a good solution.
First install the two packages you need
https://github.com/go-playground/locales
https://github.com/go-playground/universal-translator
Perform:
go get github.com/go-playground/universal-translator
go get github.com/go-playground/locales
Example:
package main
import (
"fmt"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
type Users struct {
Name string `form:"name" json:"name" validate:"required"`
Age uint8 `form:"age" json:"age" validate:"required,gt=18"`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Name: "admin",
Age: 12,
Passwd: "123",
Code: "123456",
}
uni := ut.New(zh.New())
trans, _ := uni.GetTranslator("zh")
validate := validator.New()
// Validators register translators
err := zh_translations.RegisterDefaultTranslations(validate, trans)
if err!=nil {
fmt.Println(err)
}
err = validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Translate(trans))//Age Must be greater than 18
return
}
}
return
}
Output:
Age must be greater than 18
So far we found that most of the error message has been translated into Chinese, but the field name (Age) or without translation, in order to bring the field name translated into Chinese, and checked some information, https: / / www ofstack. com article / 197866. htm > .
Do not succeed (there may be omission), or finally looked at the 1 under the source code, in
<
https: / / github com/go - playground/validator/blob/master/validator_instance go, line 137
// RegisterTagNameFunc registers a function to get alternate names for StructFields.
//
// eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names:
//
// validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
// if name == "-" {
// return ""
// }
// return name
// })
The idea is to register a function that takes the Chinese name added to struct tag as an alternate.
package main
import (
"fmt"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"reflect"
)
type Users struct {
Name string `form:"name" json:"name" validate:"required" label:" The user name "`
Age uint8 `form:"age" json:"age" validate:"required,gt=18" label:" age "`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Name: "admin",
Age: 12,
Passwd: "123",
Code: "123456",
}
uni := ut.New(zh.New())
trans, _ := uni.GetTranslator("zh")
validate := validator.New()
// registered 1 A function, get struct tag It's custom label As the field name
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name:=fld.Tag.Get("label")
return name
})
// Register the translator
err := zh_translations.RegisterDefaultTranslations(validate, trans)
if err!=nil {
fmt.Println(err)
}
err = validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Translate(trans))// Must be older than 18
return
}
}
return
}
Output results:
Must be older than 18
The validator is built into the gin
gin already supports go-playground/validator/v10 for verification. See the full documentation on label usage here.
Only 1 example of binding ShouldBindWith is provided below; for more information, go here.
The sample
package main
import (
"fmt"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"net/http"
"reflect"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
var trans ut.Translator
// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" json:"check_in" binding:"required,bookabledate" time_format:"2006-01-02" label:" Input time "`
CheckOut time.Time `form:"check_out" json:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02" label:" The output of time "`
}
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
date, ok := fl.Field().Interface().(time.Time)
if ok {
today := time.Now()
if today.After(date) {
return false
}
}
return true
}
func main() {
route := gin.Default()
uni := ut.New(zh.New())
trans, _ = uni.GetTranslator("zh")
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
// Register the translator
_= zh_translations.RegisterDefaultTranslations(v, trans)
// Register custom functions
_=v.RegisterValidation("bookabledate", bookableDate)
// registered 1 A function, get struct tag It's custom label As the field name
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name:=fld.Tag.Get("label")
return name
})
// Register translations according to the tags provided
v.RegisterTranslation("bookabledate", trans, func(ut ut.Translator) error {
return ut.Add("bookabledate", "{0} Not earlier than the current time or {1} Invalid format !", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("bookabledate", fe.Field(), fe.Field())
return t
})
}
route.GET("/bookable", getBookable)
route.Run(":8085")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
errs := err.(validator.ValidationErrors)
fmt.Println(errs.Translate(trans))
//for _, e := range errs {
// // can translate each error one at a time.
// fmt.Println(e.Translate(trans))
//}
c.JSON(http.StatusBadRequest, gin.H{"error": errs.Translate(trans)})
}
}
Run the program and execute the following command
$ curl "localhost:8085/bookable?check_in=2018-04-16&check_out=2018-04-16"
Results:
{"error":{"Booking. Input time ":" Input time cannot be earlier than the current time or input time format error!" ,"Booking. Output time ":" Output time must be greater than CheckIn"}}
Looking at the above results, we find that the translation is still not perfect. For example, in the case of gtfield in the rules, the field (CheckIn) is not translated. Therefore, adding label to struct does not fundamentally solve the field translation problem. To get the desired result, you need to process the error message separately and then output it.
Define the translation library first
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Users struct {
Phone string `form:"phone" json:"phone" validate:"required"`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Phone: "1326654487",
Passwd: "123",
Code: "123456",
}
validate := validator.New()
err := validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err)//Key: 'Users.Passwd' Error:Field validation for 'Passwd' failed on the 'min' tag
return
}
}
return
}
0
Redefine the translation function
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Users struct {
Phone string `form:"phone" json:"phone" validate:"required"`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Phone: "1326654487",
Passwd: "123",
Code: "123456",
}
validate := validator.New()
err := validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err)//Key: 'Users.Passwd' Error:Field validation for 'Passwd' failed on the 'min' tag
return
}
}
return
}
1
Where the original error message was translated
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Users struct {
Phone string `form:"phone" json:"phone" validate:"required"`
Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
Code string `form:"code" json:"code" validate:"required,len=6"`
}
func main() {
users := &Users{
Phone: "1326654487",
Passwd: "123",
Code: "123456",
}
validate := validator.New()
err := validate.Struct(users)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err)//Key: 'Users.Passwd' Error:Field validation for 'Passwd' failed on the 'min' tag
return
}
}
return
}
2
Modified to
msg:=TransTagName(BookingTrans,errs.Translate(trans))
fmt.Println(msg)
The results of
{"error":{"Booking. Input time ":" Input time cannot be earlier than the current time or input time format error!" ,"Booking. Output time ":" Output time must be greater than input time "}}
Summary:
1.gin has supported validator's latest v10.
2.validator data validation order struct field from top to bottom, single field rule (binding:"gt=0,lt=2 '), first left, then right.
Reference:
https://github.com/go-playground/validator
https://github.com/gin-gonic/gin
https://gitissue.com/issues/5d06a73965d56f73569b825f
https://segmentfault.com/a/1190000022527284