GO:HTTP应答头、状态码、应答体设置顺序
GO在HTTP回调处理函数中,对应答头、状态码、应答体的设置有顺序要求。
正确顺序:应答头(1)、状态码(2)、应答体(3)
正确示例:
package main
import (
"fmt"
"net/http"
"net/http/httputil"
"time"
)
func server() {
go func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
/* 设置应答头 */
w.Header().Set("Content-Type", "application/json")
/* 设置状态码 */
w.WriteHeader(201)
/* 设置应答体 */
w.Write([]byte("{}"))
})
http.ListenAndServe(":1280", nil)
}()
}
func client() {
/* 发请求收应答 */
ack, err := http.Get("http://127.0.0.1:1280/")
if err != nil {
panic(err)
}
defer ack.Body.Close()
/* 导出应答 */
ackDump, err := httputil.DumpResponse(ack, true)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", ackDump)
}
func main() {
// 服务端
server()
// 服务端异步,可能HTTP服务尚未创建好客户端已经开始连接而连接失败,需稍等
time.Sleep(time.Second*5)
// 客户端
client()
}
运行:
[test1280@test1280 demo]$ go run main.go
HTTP/1.1 201 Created
Content-Length: 2
Content-Type: application/json
Date: Fri, 13 Nov 2020 16:11:45 GMT
{}
此时,设置的状态码、应答头、应答体均成功生效。
错误示例1:先设置状态码,后设置应答头
func server() {
go func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
/* 设置状态码 */
w.WriteHeader(201)
/* 设置应答头 */
w.Header().Set("Content-Type", "application/json")
/* 设置应答体 */
w.Write([]byte("{}"))
})
http.ListenAndServe(":1280", nil)
}()
}
运行:
[test1280@test1280 demo]$ go run main.go
HTTP/1.1 201 Created
Content-Length: 2
Content-Type: text/plain; charset=utf-8
Date: Fri, 13 Nov 2020 16:17:00 GMT
{}
可以看到,Content-Type是text/plain,而非application/json。
在调用w.WriteHeader后调用w.Header().Set是无效的。
net/http/server.go
Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
错误示例2:先设置应答体,后设置状态码
func server() {
go func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
/* 设置应答头 */
w.Header().Set("Content-Type", "application/json")
/* 设置应答体 */
w.Write([]byte("{}"))
/* 设置状态码 */
w.WriteHeader(201)
})
http.ListenAndServe(":1280", nil)
}()
}
运行:
[test1280@test1280 demo]$ go run main.go
2020/11/13 08:23:37 http: superfluous response.WriteHeader call from main.server.func1.1 (main.go:20)
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json
Date: Fri, 13 Nov 2020 16:23:37 GMT
{}
注意:
- 错误输出:http: superfluous response.WriteHeader call from main.server.func1.1 (main.go:20)
- 错误状态:期待201,实得200
在调用w.Write后调用w.WriteHeader是无效的。
net/http/server.go
If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) before writing the data.
If WriteHeader is not called explicitly, the first call to Write will trigger an implicit WriteHeader(http.StatusOK).
查阅源码:
net/http/server.go
// either dataB or dataS is non-zero.
func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err error) {
……
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
……
}
可以看到:调用write时,判断如果没有调用过w.WriteHeader时,则设置200。
net/http/server.go
func (w *response) WriteHeader(code int) {
……
if w.wroteHeader {
caller := relevantCaller()
w.conn.server.logf("http: superfluous response.WriteHeader call from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line)
return
}
……
}
即,重复调用WriteHeader,只有第一次生效,其他时候无效返回。
总结:我们正确的设置顺序是:应答头(1) < 状态码(2) < 应答体(3)
参考:
1.https://golang.org/pkg/net/http/#Header.Set
2.https://www.cnblogs.com/liabio/p/11718439.html
3.https://stackoverflow.com/questions/42906490/whats-the-bad-effect-of-http-multiple-response-writeheader-calls