当我们在开发某个功能函数的时候,由于调用方的实参类型可能有多个,比如 beego 框架对 POST 请求的参数合法性的校验
func (c *BackupPlanNewController) ModifyBackupObjects(){
.......
var param msg.BackupObjectsPtr //这可以是任何别的结构体,只要实现了 Valid 方法
if err := json.Unmarshal(c.Ctx.Input.RequestBody, ¶m); err != nil {
c.RenderFailResponse(400, err.Error())
return
}
valid := validation.Validation{}
b, err := valid.Valid(¶m)
......
}
在 valid.Valid(¶m) 函数里是怎么调用到 param 的 Valid 方法的,param 的类型可能是多种多样的。分析 valid.Valid 方法发现:
// Valid Validate a struct.
// the obj parameter must be a struct or a struct pointer
func (v *Validation) Valid(obj interface{}) (b bool, err error) {
objT := reflect.TypeOf(obj)
objV := reflect.ValueOf(obj)
switch {
case isStruct(objT):
case isStructPtr(objT):
objT = objT.Elem()
objV = objV.Elem()
default:
err = fmt.Errorf("%v must be a struct or a struct pointer", obj)
return
}
for i := 0; i < objT.NumField(); i++ {
var vfs []ValidFunc
if vfs, err = getValidFuncs(objT.Field(i)); err != nil {
return
}
var hasRequired bool
for _, vf := range vfs {
if vf.Name == "Required" {
hasRequired = true
}
currentField := objV.Field(i).Interface()
if objV.Field(i).Kind() == reflect.Ptr {
if objV.Field(i).IsNil() {
currentField = ""
} else {
currentField = objV.Field(i).Elem().Interface()
}
}
chk := Required{""}.IsSatisfied(currentField)
if !hasRequired && v.RequiredFirst && !chk {
if _, ok := CanSkipFuncs[vf.Name]; ok {
continue
}
}
if _, err = funcs.Call(vf.Name,
mergeParam(v, objV.Field(i).Interface(), vf.Params)...); err != nil {
return
}
}
}
if !v.HasErrors() {
if form, ok := obj.(ValidFormer); ok { //其实关键的就是这里的类型强转
form.Valid(v)
}
}
return !v.HasErrors(), nil
}
-------------------
// ValidFormer valid interface
type ValidFormer interface {
Valid(*Validation)
}
可以看到关键的是把参数进行了一次类型强转,把 interface{} 转换为了 ValidFormer 接口就可以了,在以后的编码中可以借鉴下。