go反射校验struct需先判指针再isnil,非指针用iszero;自定义validate tag替代json,缓存type和规则提升性能;递归校验须限深度并严格按kind分支处理。
Go 反射校验 struct 字段是否为空值
反射做通用校验的核心,是绕过编译期类型约束,动态读取 struct 字段的值和 tag。但 reflect.Value.Interface() 在 nil 指针上调用会 panic,这是最常踩的坑。
实操建议:
- 必须先用
v.Kind() == reflect.Ptr判断是否为指针,再用v.IsNil()检查是否为空指针,不能直接v.Elem() - 对非指针字段,用
v.IsZero()判空更安全(它按类型语义判断:0、””、nil slice/map/interface{} 都算空) - 注意
time.Time、自定义类型等可能重写了IsZero()方法,需确认其行为是否符合业务“空”的定义
用 struct tag 控制字段是否参与校验
只靠 json: tag 不够灵活,比如某些字段前端可不传但后端必填,或需要区分“创建”和“更新”场景。得自己定义校验 tag,例如 validate:"required,phone"。
实操建议:
- 解析 tag 时用
structtag包(标准库go/parser太重),避免手写正则拆分 - tag 值建议用逗号分隔,每个子项用冒号分隔 key/value,如
validate:"required,min=6,max=20",方便扩展 - 若字段有
omitempty且值为空,json.Marshal会忽略它,但反射校验仍应检查——二者语义不同,别混用
性能敏感场景下避免每次反射遍历
高频接口里反复调用反射校验,会明显拖慢吞吐。reflect.TypeOf() 和 reflect.ValueOf() 开销不小,尤其嵌套深、字段多时。