必须显式调用 r.ParseForm() 或 r.ParseMultipartForm() 才能解析表单;漏调则 r.FormValue() 返回空;Body 只可读一次,须先解析再取值;文本字段用 r.FormValue(),多值用 r.Form["key"],文件上传需 ParseMultipartForm()。

如何用 net/http 正确解析表单提交的用户资料
Go 默认不自动解析 multipart 表单(含文件上传)和普通 application/x-www-form-urlencoded,必须显式调用 r.ParseForm() 或 r.ParseMultipartForm()。漏掉这步会导致 r.FormValue("name") 始终返回空字符串。
常见错误是只在 POST 路由里读 r.Body 一次,再调用 ParseForm() 就失效了——因为 Body 是单次读取流。正确做法是:先调用 ParseForm(),再用 r.FormValue() 取值。
- 普通文本字段(如
name、email)直接用r.FormValue("name") - 多选框或重复键名(如
hobby=reading&hobby=swimming)用r.Form["hobby"]获取[]string - 上传头像等文件时,必须用
r.ParseMultipartForm(32 并检查err == nil,否则r.MultipartForm为nil
更新用户资料前必须校验哪些字段
仅靠前端限制毫无意义。后端至少需验证:email 格式与唯一性、username 长度与字符合法性、password(若提供)是否满足最小长度且未明文存储。跳过校验可能引发 SQL 注入、邮箱劫持或越权修改。
示例中常犯错的是把密码字段也无条件更新进数据库——实际应只在用户主动提交新密码时才哈希并更新 password_hash 字段,否则保留原值。
立即学习“go语言免费学习笔记(深入)”;
- 用
net/mail.ParseAddress(r.FormValue("email"))初筛邮箱格式 - 查库确认该
email是否已被他人占用(排除自身 ID) - 对
username使用regexp.MustCompile(`^[a-zA-Z0-9_]{3,20}$`)限制 - 若
r.FormValue("password") != "",才调用bcrpyt.GenerateFromPassword()重新哈希
用 database/sql 安全更新用户记录的写法
拼接 SQL 字符串更新用户资料等于邀请 SQL 注入。必须用参数化查询,且只更新明确允许的字段。别写 UPDATE users SET "+field+" = ? 这种动态字段名——字段名不能参数化,只能白名单控制。
stmt := `UPDATE users SET name = ?, email = ?, bio = ?, updated_at = NOW() WHERE id = ? AND status = 'active'` _, err := db.Exec(stmt, name, email, bio, userID)
注意点:
-
WHERE条件必须包含id和状态检查(如status = 'active'),防止越权批量更新 - 不要在
UPDATE中包含password_hash字段,除非已确认新密码非空并完成哈希 - 执行后务必检查
err和rowsAffected(sql.Result.RowsAffected()),为 0 表示用户不存在或被禁用
为什么 HTTP 重定向必须用 http.Redirect 而不是手动设状态码
表单提交后若直接返回 HTML 页面,会留下“刷新重复提交”隐患。必须用 303 See Other 重定向到详情页。但很多人手写 w.WriteHeader(http.StatusSeeOther) + w.Header().Set("Location", "/profile"),这容易漏掉 Content-Type 清空或浏览器兼容问题。
http.Redirect() 内部已处理好这些细节,并默认使用 303(POST 后重定向的标准状态码)。用错成 302 可能在某些客户端导致重复 POST。
- 始终传
http.StatusSeeOther作为第 4 个参数,别依赖默认值 - 重定向目标 URL 必须是绝对路径(如
"/profile"),相对路径在某些代理下会出错 - 重定向前确保没有写入任何响应体,否则会 panic:“http: multiple response.WriteHeader calls”
字段校验失败时渲染表单页可以返回 200,但成功更新后这一跳绝不能省——否则用户刷新页面就会二次提交。










