本文共 3958 字,大约阅读时间需要 13 分钟。
今天遇到个怪事,死活读不到post值,类似的代码如下
func test(w http.ResponseWriter,r *http.Request){ // 原本是读取一个html文件输出,这里使用一个长切片来说明问题 // m, err := ioutil.ReadFile("./test.html") // if err != nil { // fmt.Println("Read file failed, err:", err) // return // } var m []byte //如果i<1000改成i<10,则不会出现问题 for i := 0; i < 1000; i++ { m = append(m, []byte("hello, world")...) } // 输出 _, err := w.Write(m) // 解析表单并打印 err = r.ParseForm() if err != nil { fmt.Println("parse form failed, err:", err) // 先不退出 // return } fmt.Println("request map:", r.Form) fmt.Println("post map:", r.PostForm)}func main(){ http.HandleFunc("/test", test) err := http.ListenAndServe("http://127.0.0.1:8088",nil) if err != nil { fmt.Println("serve failed, err:", err) return }}
此时浏览器打开一个页面,页面内容为
提交表单后,golang控制台打印仅有a和b的值,c无法获取,并且有提示无法解析form的情况
parse form failed, err: http: invalid Read on closed Bodyrequest map: map[a:[1] b:[2]]post map: map[]
到底是原因呢,总感觉是卡在.write()
上了,于是看了一下源码注释
// Write writes the data to the connection as part of an HTTP reply. // // If WriteHeader has not yet been called, Write calls // WriteHeader(http.StatusOK) before writing the data. If the Header // does not contain a Content-Type line, Write adds a Content-Type set // to the result of passing the initial 512 bytes of written data to // DetectContentType. Additionally, if the total size of all written // data is under a few KB and there are no Flush calls, the // Content-Length header is added automatically. // // Depending on the HTTP protocol version and the client, calling // Write or WriteHeader may prevent future reads on the // Request.Body. For HTTP/1.x requests, handlers should read any // needed request body data before writing the response. Once the // headers have been flushed (due to either an explicit Flusher.Flush // call or writing enough data to trigger a flush), the request body // may be unavailable. For HTTP/2 requests, the Go HTTP server permits // handlers to continue to read the request body while concurrently // writing the response. However, such behavior may not be supported // by all HTTP/2 clients. Handlers should read before writing if // possible to maximize compatibility. Write([]byte) (int, error)
谷歌翻译如下(凭感觉先看一下,再细品~~):
Write将数据作为HTTP回复的一部分写入连接。
如果尚未调用WriteHeader,则调用Write
写入数据之前,使用WriteHeader(http.StatusOK)。如果标题 不包含Content-Type行,Write添加了Content-Type集 将初始的512字节的写入数据传递到的结果 DetectContentType。另外,如果全部写总大小 数据不到几KB,并且没有Flush调用, Content-Length标头会自动添加。
根据HTTP协议版本和客户端,调用
Write或WriteHeader可能会阻止以后读取 Request.Body。对于HTTP / 1.x请求,处理程序应读取任何内容 写入响应之前需要的请求正文数据。一旦 标头已被刷新(由于显式的Flusher.Flush 调用或写入足够的数据以触发刷新),请求正文 可能不可用。对于HTTP / 2请求,Go HTTP服务器允许 处理程序以继续并发读取请求主体 撰写回复。但是,可能不支持这种行为 通过所有HTTP / 2客户端。处理程序应在写入之前阅读,如果 可能最大化兼容性。
果然,.write()
是有容量限制的。
原因:
注释第二段第四行写明了当超出了512字节的限制就可能会影响以后的读取Request.Body
。 解决办法:
在第三段的第三行中,在写入前先读取。func test(w http.ResponseWriter,r *http.Request){ u := r.URL h := r.Header b, _ := ioutil.ReadAll(r.Body) // 打印地址 fmt.Println("url:", u) // 打印header fmt.Println("header:", h) // 打印body fmt.Println("body:", string(b)) // 解析表单并打印 err := r.ParseForm() if err != nil { fmt.Println("parse form failed, err:", err) // 先不退出 // return } fmt.Println("request map:", r.Form) fmt.Println("post map:", r.PostForm) var m []byte for i := 0; i < 1000; i++ { m = append(m, []byte("hello, world")...) } // 输出 _, err = w.Write(m)}
打印
url: /test?a=1&b=2header: map[Accept:[...] Accept-Encoding:[gzip, deflate] Accept-Language:[...] Connection:[keep-alive] Content-Length:[14] Content-Type:[application/x-www-form-urlencoded] Cookie:[...] Origin:[http://127.0.0.1:8088] Referer:[http://127.0.0.1:8088/1.html] Upgrade-Insecure-Requests:[1] User-Agent:[...]]body: c=3request map: map[a:[1] b:[2] c:[3]]post map: map[c:[3]]
综合原文注释和打印的结果,可以认为golang在获得get值的时候是从url中取值,而获得post值的时候是从body中取值,这也是为什么在.write()
溢出后,get取值正常,post却无法取值。
在遇到只有get值时尽量使用.URL.Query()
,而不是用.form
引申:要准确获取.Form
或.PostForm
的值之前,需要先进行.ParseForm()
操作,并判断是否正常。
转载地址:http://mvkpi.baihongyu.com/