golang中设置http头用 w.Header().Set() ,设置状态码用 w.WriteHeader() , 设置body用 w.Write() 。但他们的调用顺序是有要求的。正确的调用顺序如下:
func HandleHello(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-name","john")
w.WriteHeader(http.StatusOK)
w.Write([]byte("hello world!"))
}
分析源码
我们来看看http.response中status code 和 header 相关的数据结构。golang源码的版本如下:
go version go1.15.7 darwin/amd64
response
// net/http/server.go:418
// A response represents the server side of an HTTP response.
type response struct {
//...
wroteHeader bool // reply header has been (logically) written
w *bufio.Writer // buffers output in chunks to chunkWriter
cw chunkWriter
// handlerHeader is the Header that Handlers get access to,
// which may be retained and mutated even after WriteHeader.
// handlerHeader is copied into cw.header at WriteHeader
// time, and privately mutated thereafter.
handlerHeader Header
//...
}
- wroteHeader 是否已经设置过http头的标记
- w 写入到chunkWriter的writer
- cw 内容会被输出到响应连接的buffer
- handlerHeader
WriteHeader()
// net/http/server.go:1133
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
}
checkWriteHeaderCode(code)
w.wroteHeader = true
w.status = code
if w.calledHeader && w.cw.header == nil {
w.cw.header = w.handlerHeader.Clone()
}
//...
}
如果已经设置过header,那么打印一行错误日志后直接返回。如果没有设置过header,先设置已经写入header的标记 w.wroteHeader = true 。接着把 w.handlerHeader 的内容拷贝进 chunkWriter 。可知,设置状态码接口 WriteHeader() 只有第一次调用才是有效的,并且该接口把header写入到输出buffer中。所以 w.Header().Set() 必须在能在 WriteHeader() 之前调用。
write()
// net/http/server.go:1573
// 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)
}
//...
if dataB != nil {
return w.w.Write(dataB)
} else {
return w.w.WriteString(dataS)
}
}
如果write()之前没有设置过状态码,则默认调用 WriteHeader() 设置状态码为200 OK。但 WriteHeader() 只能第一次是有效调用,所以调用***write()*** 后再调用 *WriteHeader() 是无效的。
结论通过对源码的分析,设置http的头,状态码,body的顺序如下:w.Header().Set() ,w.WriteHeader() ,w.Write() 。