reflect校验易panic因访问未导出字段、空指针或类型不匹配时直接panic;需用v.CanInterface()判断、解引用前检查指针有效性,并按Kind分支处理值以避免类型丢失,再递归校验嵌套结构与slice。
reflect 校验容易 panic?因为 Go 反射在访问未导出字段、空指针或类型不匹配时直接 panic,而不是返回错误。比如对 struct 中小写字段调用 Field(i).Interface() 会触发 panic: reflect.Value.Interface: cannot return value obtained from unexported field。
v.CanInterface() 判断是否可安全取值,避免 panicv.Elem() 解引用,但必须先检查 v.Kind() == reflect.Ptr && !v.IsNil()
reflect.StructTag 提取校验规则?Go 结构体 tag 是最自然的校验元数据载体,比如 `validate:"required,min=3,max=20"`。关键不是解析字符串,而是统一提取逻辑——所有字段都走 structField.Tag.Get("validate"),再交给独立的解析器处理。
= 时,按布尔规则处理(如 "required" 表示必填)"min=5
" → map[string]string{"min": "5"})reflect.Value.Interface() 导致类型丢失?校验逻辑常需比较原始值,比如判断 int 是否超限、string 长度是否合规。但 v.Interface() 返回 interface{},直接断言易出错;更稳妥的是按 v.Kind() 分支处理。
switch v.Kind() {
case reflect.String:
s := v.String()
if len(s) < minLen || len(s) > maxLen {
return errors.New("string length out of range")
}
case reflect.Int, reflect.Int64:
i := v.Int()
if i < minInt || i > maxInt {
return errors.New("integer out of range")
}
case reflect.Ptr:
if !v.IsNil() {
return validateValue(v.Elem()) // 递归校验指针指向的值
}
}
通用校验必须支持深度遍历。对 struct 类型字段,递归调用主校验函数;对 slice 或 array,逐项校验每个元素——但要注意:只校验元素值本身,不校验 slice 长度限制(那是 tag 控制的)。
v.Kind() == reflect.Struct → 递归调用校验入口函数v.Kind() == reflect.Slice || v.Kind() == reflect.Array → 遍历 v.Len() 次,对 v.Index(i) 校验nil slice 或 map,若 tag 无 required 则跳过;否则报错