应根据HTTP请求类型选择标签:JSON请求用json标签,表单请求用form标签;若需同时支持两种格式,结构体字段须同时声明json和form标签,如Username string json:"username" form:"username"`。

用 json 标签还是 form 标签?看 HTTP 请求类型
如果前端发的是 JSON(Content-Type: application/json),用 json 标签;如果是 HTML 表单提交(Content-Type: application/x-www-form-urlencoded 或 multipart/form-data),必须用 form 标签,否则 ParseForm 和 Decode 都会静默失败。
常见错误现象:结构体字段始终为零值,打印 r.Form 却能看到原始键值对——说明标签没对上。
-
json标签只影响json.Unmarshal,对表单解析完全无效 -
form标签是net/url.Values解析时唯一识别的字段映射依据 - 若同时支持两种格式,结构体需同时声明
json和form标签,例如:Username string `json:"username" form:"username"`
用 r.ParseForm() 之前必须检查 r.Method 和 Content-Type
Go 的 ParseForm 对非 POST/PUT/PATCH 方法直接返回 nil 错误(但不 panic),且对 GET 请求默认忽略 body;更隐蔽的是,如果请求头没带正确的 Content-Type,ParseForm 会把整个 body 当作 raw query 解析,导致字段丢失。
- 务必先写
if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed); return } - 对于
multipart/form-data(含文件上传),ParseForm必须在ParseMultipartForm之后调用,否则文件字段不可见 - 调用
r.ParseForm()后,再通过r.PostForm读取,而非r.Form(后者包含 URL 查询参数,可能污染)
用 schema 包还是手写 Decode?别碰 reflect.StructTag 手动解析
标准库没有内置表单到结构体的深度绑定,url.Values 到 struct 需要借助第三方包或手动映射。最轻量可靠的选择是 go-playground/form(原 go-playground/schema 的继任者),它支持嵌套、切片、时间格式、自定义解码器,且无运行时依赖。
示例:
import "github.com/go-playground/form/v4" decoder := form.NewDecoder() err := decoder.Decode(&user, r.PostForm)
- 避免用
mapstructure:它默认按map[string]interface{}工作,对url.Values(本质是map[string][]string)支持差,易丢数组第二项 - 别自己遍历
r.PostForm+reflect:字段类型转换(如int、time.Time)逻辑复杂,边界 case 多(空字符串、多个同名字段、缺失字段默认值) -
form包默认跳过空字符串字段,如需保留零值,设置decoder.IgnoreEmpty(false)
嵌套结构体和切片怎么写 form 标签?语法必须严格匹配
HTML 表单不天然支持嵌套,靠命名约定模拟:user.Name、addresses[0].City、tags[]。对应结构体标签必须完全一致,否则解码失败且无提示。
示例结构体:
type User struct {
Name string `form:"name"`
Profile Profile `form:"profile"`
Tags []string `form:"tags[]"`
}
type Profile struct {
Age int `form:"age"`
City string `form:"city"`
}
- 嵌套字段用点号分隔,如
Profile.Age→form:"profile.age"(注意小写) - 切片用
[]后缀,HTML 中多个同名字段(<input name="tags[]">)会被自动聚合 - 数组索引形式(
addresses[0].City)需启用decoder.SetStrictMode(true)并配合form:"addresses[].city",否则默认只认[]形式 - 所有嵌套层级的结构体字段都必须导出(首字母大写),否则反射无法访问
form 标签和 HTML name 属性的大小写与拼写一致性,以及对 multipart/form-data 场景下 ParseMultipartForm 和 ParseForm 调用顺序的混淆。

















