r.ParseForm()必须在读取r.PostForm前调用,因Go默认不自动解析表单;需显式触发,否则导致空值或panic,并影响Content-Type区分与参数合并。
Go HTTP 处理表单提交时为什么 r.ParseForm() 必须在读取 r.PostForm 前调用
不调用 r.ParseForm() 就直接访问 r.PostForm 或 r.FormValue("username"),会导致空值或 panic —— 因为 Go 的 http.Request 默认不会自动解析表单数据,必须显式触发解析。它还会根据 Content-Type 自动区分 application/x-www-form-urlencoded 和 multipart/form-data(如含文件上传),但前提是请求头正确且已调用该函数。
实操建议:
- 所有处理 POST 表单的 handler 开头第一行应是
if err := r.ParseForm(); err != nil { ... } - 若前端用
fetch提交 JSON,后端不能依赖r.FormValue,而要用json.Decoder读取r.Body -
r.ParseForm()会把 query string 和 body 表单合并进r.Form,注意同名参数覆盖顺序(body 优先)
用 database/sql 插入用户数据时如何安全防 SQL 注入
拼接字符串构造 SQL(如 "INSERT INTO users VALUES ('" + username + "')" )在 Go 中一样危险,且无法利用数据库连接池预编译优化。Go 的 database/sql 原生支持参数化查询,Exec 和 Query 方法都接受 ... 可变参数传入值,底层由驱动完成绑定。
实操建议:
- 永远使用
?, $1, $2占位符(取决于驱动:MySQL 用?,PostgreSQL 用$1) - 密码绝不能明文存库,必须用
golang.org/x/crypto/bcrypt先哈希:hashed, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
- 插入后检查
result.RowsAffected()确认是否成功写入,而非只看 error 是否为 nil
前端 HTML 表单提交到 Go 后端时常见的 404 或 405 错误原因
Go 的 http.ServeMux 是严格匹配路径和方法的:注册了 http.HandleFunc("/register", handler),但前端 form 的 action 写成 /register/(多斜杠)或用 GET 提交,就会 404 或 405。另外,如果 handler 只处理 POST,却没对其他方法返回明确提示,浏览器可能缓存旧请求方式。
实操建议:
- form 的
method="POST"必须与 handler 中判断的r.Method == "POST"一致 - 路径注册用
"/register",前端 action 也保持完全一致,避免尾部斜杠差异 - 在 handler 开头加判断:
if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return }
为什么用 net/http 做注册接口时,不应该在 handler 里直接操作全局 *sql.DB
全局 *sql.DB 本身是并发安全的,可以被多个 goroutine 共享,但问题出在「直接裸用」容易导致连接泄漏或事务失控。比如忘记调用 rows.Close()、在 handler 中开启事务却不统一 commit/rollback、或错误地复用 sql.Tx 跨请求。
实操建议:
- 把
*sql.DB作为依赖注入 handler,例如闭包封装:func makeRegisterHandler(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ... } } - 涉及事务时,务必用
defer tx.Rollback()并在成功后显式tx.Commit() - 不要在 handler 中长期持有
sql.Rows或未关闭的sql.Tx,它们会阻塞连接池
r.ParseForm() 的调用位置和 bcrypt 的哈希步骤,这两处最容易被跳过或写反。

参数合并。






