r.ParseForm()

具体原因很简单,看下面这个示例代码就知道了:

Bug 重现

package main

import (
    "net/http"
)

func HelloServer1(w http.ResponseWriter, r *http.Request) {
    //r.ParseForm()
    val := r.FormValue("key")
    println("HelloServer1", val)
    w.Write([]byte(val))
}

func HelloServer2(w http.ResponseWriter, r *http.Request) {
    //r.ParseForm()
    _, fe := r.MultipartReader()
    if fe != nil {
        println("HelloServer2", fe)
        return
    }
    val := r.FormValue("key")
    println("HelloServer2", val)
    w.Write([]byte(val))
}
func HelloServer3(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    _, fe := r.MultipartReader()
    if fe != nil {
        println("HelloServer3", fe)
        return
    }
    val := r.FormValue("key")
    println("HelloServer3", val)
    w.Write([]byte(val))
}

func main() {
    http.HandleFunc("/hello1", HelloServer1)
    http.HandleFunc("/hello2", HelloServer2)
    http.HandleFunc("/hello3", HelloServer3)
    println("start ...")
    err := http.ListenAndServe(":8888", nil)
    if err != nil {
        println("error", err)
    }
}

三次POST请求的结果分别如下:

  1. 返回结果正确
curl -F "name=yanyiwu" "localhost:8888/hello1?key=value1"
value1

虽然在调用 r.FormValue 之前没有调用过 r.ParseForm , 但是返回的结果正确。 因为在 r.FormValue 函数之内当 r.Form == nil 时会先调用 ParseMultipartForm 。

  1. 返回结果错误
curl -F "name=yanyiwu" "localhost:8888/hello2?key=value2"
http: multipart handled by MultipartReadergolang/go/src/net/http/request.go
  1. 返回结果正确
curl -F "name=yanyiwu" "localhost:8888/hello3?key=value3"
value3
r.ParseForm()

解决方案

在调用 r.FormValue 之前记得 r.ParseForm 即可。

go 的 net/http 源码还可以更完善一点

其实我个人看来,go源码里面 r.FormValue 函数里面调用 ParseMultipartForm 的时候,没有检查 ParseMultipartForm 的返回值 是不太完善的做法。 个人觉得应该检查返回值,虽然无法将错误返回(因为 r.FormValue 的返回值只有一个,是 string 类型), 但是可以将错误打印出来,以此提醒开发者。

pull request