query参数必须用r.URL.Query()解析,它自动处理URL解码、重复键等;路径参数需依赖chi等路由库;JSON请求须用json.NewDecoder(r.Body)流式解码,不可用ParseForm()。

query参数用 r.URL.Query() 解析最稳妥
URL 中的 query 参数(?name=alice&age=30)必须通过 r.URL.Query() 获取,而不是手动解析 r.URL.RawQuery 或拼接字符串。这个方法返回 url.Values(本质是 map[string][]string),自动处理 URL 解码、重复键、空值等边界情况。
-
r.URL.Query().Get("name")返回第一个匹配值(适合单值场景),不 panic -
r.URL.Query()["name"]返回完整切片,可判断是否存在、是否多值 - 若参数含中文或特殊字符(如
?q=Go%2BDev),Get()会自动解码为"Go+Dev";手撕strings.Split(r.URL.RawQuery, "&")会漏解码,导致乱码 - 注意:
r.ParseForm()不影响r.URL.Query(),两者独立 —— query 始终走 URL 解析,form 数据才走 body 解析
r.ParseForm() 和 r.ParseMultipartForm() 的触发时机很关键
调用 r.ParseForm() 才能访问 r.Form 和 r.PostForm。但它的行为取决于请求 method 和 content-type:
- GET/HEAD 请求:调用后
r.Form合并了 query + body(但 GET 通常无 body,所以基本等于 query) - POST/PUT 等:若
Content-Type是application/x-www-form-urlencoded,r.ParseForm()解析 body;若是multipart/form-data,必须先调用r.ParseMultipartForm(maxMemory),否则r.PostForm为空 - 未调用任何 Parse 方法时,
r.FormValue("key")会隐式调用r.ParseForm(),但仅限于 urlencoded 场景;对 multipart 会静默失败,返回空字符串 - 多次调用
ParseForm()没副作用,但ParseMultipartForm()多次调用会 panic:http: multipart already parsed
JSON 请求体不能靠 ParseForm(),得用 json.Decoder
当前端发的是 Content-Type: application/json,比如 {"user_id":123,"tags":["a","b"]},r.ParseForm() 完全无效 —— 它只认表单编码,不认识 JSON 结构。
- 必须读取
r.Body并用json.NewDecoder(r.Body).Decode(&v)解析 - 注意:
r.Body只能读一次;如果之前调用了r.ParseForm()或r.FormValue(),它们内部已消费了 body,再读就是空的 —— 此时需用r.Body = ioutil.NopCloser(bytes.NewReader(data))重置(Go 1.16+ 推荐用io.ReadAll+ 重设) - 别用
json.Unmarshal(io.ReadAll(r.Body), &v)一次性读全部,容易被超大 payload OOM;应直接传r.Body给json.NewDecoder流式解码
路径参数(URL path segment)要靠 chi 或 gorilla/mux 这类路由库
标准 net/http 不解析路径参数(如 /users/:id 中的 :id)。必须借助第三方路由器,否则只能手动 strings.Split(r.URL.Path, "/") —— 这种方式脆弱且无法处理嵌套路由、可选段、正则约束。
立即学习“go语言免费学习笔记(深入)”;
-
chi.URLParam(r, "id")直接取命名参数,自动处理 URL 解码 -
gorilla/mux.Vars(r)["id"]同理,但要求提前用Router.HandleFunc("/users/{id}", ...).Methods("GET") - 若坚持不用路由库,至少用
path.Clean(r.URL.Path)防止../路径遍历,再配合strings.TrimPrefix提取末段,但无法区分/api/v1/users/123和/api/v1/users/new
func handler(w http.ResponseWriter, r *http.Request) {
// ✅ 正确:取 query
q := r.URL.Query().Get("q")
// ✅ 正确:取 POST 表单(x-www-form-urlencoded)
if err := r.ParseForm(); err == nil {
user := r.FormValue("user")
}
// ✅ 正确:取 JSON body
var req struct{ ID int `json:"id"` }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid json", http.StatusBadRequest)
return
}
// ✅ 正确:取路径参数(使用 chi)
id := chi.URLParam(r, "id")
}
query 参数和路径参数来源不同、解析机制不同,混用 FormValue 和 URLParam 是常见错误起点;body 类型决定该走 form 解析还是 JSON 解码,错配就会拿不到数据。










