2021SC@SDUSC
introduction
In the sduoj project, the server needs to receive requests from the front end and perform different business processing according to different requests. Without a normalized constraint, the server may receive unreasonable requests. In order to avoid the confusion of business logic caused by parameter errors, we need an interface verification mechanism to return these unreasonable requests.
The interface input parameter verification rules are written on the field label of the corresponding verification structure. In CreateTagRequest
, we see that it requires three request body parameters, namely Name
, , CreatedBy
and State
. In the field label, form
the parameter name is stored in , and the binding
rule constraint is stored.
type CreateTagRequest struct {
Name string `form:"name" binding:"required,min=3,max=100"`
CreatedBy string `form:"created_by" binding:"required,min=3,max=100"`
State uint8 `form:"state,default=1" binding:"oneof=0 1"`
}
The following are common label meanings.
Label | meaning |
---|---|
require | Required |
gt | more than the |
gte | greater or equal to |
lt | less than |
lte | less than or equal to |
min | minimum |
max | maximum value |
oneof | one of the parameters in the set |
len | The length requirement is the same as that given by len |
Source code analysis
BindAndValid
The method is mainly responsible for error handling, and its parameter binding and verification work is placed c.ShouldBind
in it. In error handling, the method obtains the corresponding value from the context, trans
performs certain language conversion work on the error information, and stores the error information ValidErrors
in .
func BindAndValid(c *gin.Context, v interface{
}) (bool, ValidErrors) {
var errs ValidErrors
err := c.ShouldBind(v)
if err != nil {
v := c.Value("trans")
trans, _ := v.(ut.Translator)
verrs, ok := err.(val.ValidationErrors)
if !ok {
return false, errs
}
for key, value := range verrs.Translate(trans) {
errs = append(errs, &ValidError{
Key: key,
Message: value,
})
}
return false, errs
}
return true, nil
}
validErrors
Yes []*validError
alias, here we implement the *ValidError
methods Error
, and the error
interface has only Error
methods, that is, *ValidError
implements the error
interface.
type ValidError struct {
Key string
Message string
}
type ValidErrors []*ValidError
func (v *ValidError) Error() string {
return v.Message
}
ShouldBind
Context
The parameter binding engine will be automatically selected according to the type. For example, if our Context
type is application/json, we will choose the JSON binding engine.
func (c *Context) ShouldBind(obj interface{
}) error {
b := binding.Default(c.Request.Method, c.ContentType())
return c.ShouldBindWith(obj, b)
}
ShouldBindWith
A specific binding engine will be used to bind the passed struct pointer.
func (c *Context) ShouldBindWith(obj interface{
}, b binding.Binding) error {
return b.Bind(c.Request, obj)
}
Value
The method returns a key
value associated with the context. It first checks key
whether the value is 0, and if so, c.Request
returns it; if not, it asserts that it key
is a string type and returns the relevant value, and if it key
is not a string type, it returns nil
.
func (c *Context) Value(key interface{
}) interface{
} {
if key == 0 {
return c.Request
}
if keyAsString, ok := key.(string); ok {
val, _ := c.Get(keyAsString)
return val
}
return nil
}
Get
The method returns the given key
mapped value, which involves a lock and unlock operation, which ensures that the operation of obtaining this value is atomic.
func (c *Context) Get(key string) (value interface{
}, exists bool) {
c.mu.RLock()
value, exists = c.Keys[key]
c.mu.RUnlock()
return
}
The validator
default error message in English is English, but our error message does not have to be in English, it can be changed to Simplified Chinese here. Here is a custom middleware, according to the locale
language selected, and then trans
stored in the context.
func Translations() gin.HandlerFunc {
return func(c *gin.Context) {
uni := ut.New(en.New(), zh.New(), zh_Hant_TW.New())
locale := c.GetHeader("locale")
trans, _ := uni.GetTranslator(locale)
v, ok := binding.Validator.Engine().(*validator.Validate)
if ok {
switch locale {
case "zh":
_ = zh_translations.RegisterDefaultTranslations(v, trans)
case "en":
_ = en_translations.RegisterDefaultTranslations(v, trans)
default:
_ = zh_translations.RegisterDefaultTranslations(v, trans)
}
c.Set("trans", trans)
}
c.Next()
}
}