
用 reflect 读不到结构体字段名?检查导出规则和标签
Go 的反射默认只能访问导出(首字母大写)字段,小写字段直接被忽略,不是 bug,是语言设计。如果你的结构体字段像 name string 这样定义,reflect.ValueOf(v).NumField() 能看到它,但 Field(i).Name 是空的,且 Field(i).IsExported() 返回 false —— 这意味着你拿不到名字,也取不到值。
- 确保字段首字母大写:改
name为Name - 想保留小写 JSON 键?用
json:"name"标签,反射不读它,但文档生成时可手动 fallback 到该标签 - 别依赖
Field(i).Tag.Get("json")来判断字段是否该暴露——没标签时返回空字符串,不是报错,容易漏判
生成 API 文档时,怎么把 http.HandlerFunc 和路由绑定关系抽出来?
Go 标准库的 http.ServeMux 没提供公开接口列出所有注册路由,反射也拿不到内部 mux.m(它是 unexported map)。硬扫源码或 patch 标准库都不现实。
- 自己封装一层路由注册:写个
RegisterHandler(pattern string, h http.HandlerFunc, method string),把 pattern、method、handler 名(可用runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name())存进全局 slice - 避免用
http.HandleFunc直接调用,否则信息就断了 - 注意:
FuncForPC在内联函数或编译优化后可能不准,开发环境够用,生产建议配合注释标记,比如在 handler 函数上方加// @summary 用户登录
reflect.StructField.Tag.Get("doc") 怎么安全取值?别直接 .Get 后判空
很多人写 if tag := f.Tag.Get("doc"); tag != "",看起来没问题,但 Go 的 struct tag 解析其实很脆弱:多空格、换行、引号不匹配都会让 Get 返回空,而你完全不知道是“没写”还是“写错了”。
- 先用
structtag.Parse(需引入"golang.org/x/tools/go/ast/structtag")解析整个 tag 字符串,再取值,它会明确告诉你语法错误在哪 - 更轻量的做法:用正则粗筛
`.*doc:"([^"]*)".*`,至少能避开解析失败的静默丢弃 - 如果字段没写
doc标签,别强行生成 “无描述”,留空比瞎填好;前端渲染时统一 fallback 到字段名即可
为什么生成的文档里类型显示成 main.User 而不是 User?
反射拿到的 reflect.Type.Name() 对命名类型只返回本地包名下的短名(如 User),但一旦是嵌套、指针、切片或跨包类型,String() 就会带包路径,比如 *main.User 或 []github.com/x/y.Z。直接塞进文档,可读性崩塌。
立即学习“go语言免费学习笔记(深入)”;
- 用
t.Kind()分类处理:对reflect.Ptr、reflect.Slice、reflect.Map递归展开,只对reflect.Struct和reflect.Interface看PkgPath() - 若
t.PkgPath() == "main" || t.PkgPath() == "",用t.Name();否则用path.Base(t.PkgPath()) + "." + t.Name() - 注意:内建类型如
string、int64的PkgPath()为空,Name()也是空,得靠Kind().String()回退
类型字符串拼接看着简单,但嵌套三层以上指针+切片时,手写逻辑很容易漏 case。真要健壮,不如抄一份 go/types 包里的 Type.String() 简化版,它本来就是干这个的。










