结构体字段标签无法过滤脏数据,因json:"-"和omitempty仅影响序列化输出而不校验输入;输入侧需绑定前预检,输出侧应定义专用响应struct,全链路需分层隔离脏数据。

直接用 struct 字段标签过滤脏数据会出问题
不能靠 json:"-" 或 json:",omitempty" 拦截敏感字段或格式异常的数据——这些只影响序列化输出,不改变原始值,也不校验输入合法性。比如 User.Password 设了 json:"-",但若 handler 里误用了 u.Password 做校验,照样泄露;omitempty 还会让 0、false、空字符串被静默丢弃,导致业务逻辑错乱。
输入侧过滤必须在绑定前完成
HTTP 请求体解析(如 c.BindJSON())是脏数据第一道入口,一旦绑定进 struct,就默认“信任”了。正确做法是在绑定前做轻量预检:
- 用
io.LimitReader(c.Request.Body, 2<sup>20</sup>)限制最大请求体大小,防超大 payload 耗尽内存 - 对关键字段(如 email、phone)用正则或标准库
net/mail.ParseAddress、regexp.MustCompile提前校验格式,不合法直接返回http.StatusBadRequest - 避免在 bind 后再用反射遍历字段做“事后清洗”——既增加延迟,又可能漏掉嵌套结构里的非法值
输出侧拦截靠专用响应 struct,不是 map 删除
别在 handler 里写 delete(respMap, "password") 或 delete(respMap, "token")——这种操作难复用、易遗漏、无法静态检查。真正可控的做法是为每个接口定义专属响应 struct:
-
UserPublicResponse只含ID、Name、AvatarURL,不含PasswordHash、Email、CreatedAt - 字段赋值全部显式写出:
ID: u.ID、Name: strings.TrimSpace(u.Name),中间可插清洗逻辑(如去 HTML 标签、截断超长字段) - 不同角色用不同 struct:
UserAdminResponse多一个FailedLoginCount,编译期就能约束权限边界
隔离脏数据需分层:输入过滤 + 中间件拦截 + 沙箱执行
单一手段挡不住所有脏数据。真实场景要组合使用:
- 输入层:用 Gin 的
ShouldBindJSON配合自定义 validator tag(如validate:"email,required"),失败直接中断 - 中间件层:对含富文本的字段(如
content),用bluemonday.UbbPolicy()做 HTML 消毒,再塞进响应 struct - 执行层:若需运行用户提交的表达式或脚本(如规则引擎),必须用
exec.CommandContext启独立进程,禁用网络、限制 CPU 和内存,防止脏输入触发宿主崩溃
最易被忽略的是字段级清洗时机——比如手机号带空格或括号,应在绑定后、存库前清洗,而不是等到 API 响应时才处理;否则数据库里存的就是脏数据,后续所有读取都得重复清洗。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











