背景
測試一個調用http接口時彼乌,使用了httptest.NewServer來mock一個http服務端,在驗證響應異常狀態(tài)碼時發(fā)現(xiàn)奇怪問題渊迁,明明設置了異常狀態(tài)碼慰照,但是http.Get始終返回的狀態(tài)碼都是正常的200:
mockHttpSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
w.WriteHeader(http.StatusBadRequest)
}))
resp , err:= http.Get(mockHttpSrv.URL)
if err != nil {
return
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode) // 輸出200
分析
查閱WriteHeader的文檔時才發(fā)現(xiàn)原來當w.WriteHeader沒明確調用時,默認第一次調用Write方法時琉朽,會隱式的觸發(fā)WriteHeader(http.StatusOK):
// WriteHeader sends an HTTP response header with the provided
// status code.
//
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
//
// The provided code must be a valid HTTP 1xx-5xx status code.
// Only one header may be written. Go does not currently
// support sending user-defined 1xx informational headers,
// with the exception of 100-continue response header that the
// Server sends automatically when the Request.Body is read.
WriteHeader(statusCode int)
我的單測代碼將WriteHeader寫在了Write方法之后毒租,所以默認會先觸發(fā)了WriteHeader(http.StatusOK)。
但是箱叁,為什么后來我調用WriteHeader(http.StatusBadRequest)會無效呢墅垮,正常不是應該覆蓋前面的方法才對?耕漱!
認真閱讀了 WriteHeader源碼算色,原來WriteHeader只允許寫入一次Header(通過一個布爾變量:wroteHeader,寫入后被標志為true螟够,再次調用就直接return):
// WriteHeader implements http.ResponseWriter.
func (rw *ResponseRecorder) WriteHeader(code int) {
if rw.wroteHeader {
return
}
rw.Code = code
rw.wroteHeader = true
if rw.HeaderMap == nil {
rw.HeaderMap = make(http.Header)
}
rw.snapHeader = rw.HeaderMap.Clone()
}
這樣問題就清晰了灾梦,是我WriteHeader順序寫錯了,應該放在Write方法之前妓笙。
總結
WriteHeader只能調用一次若河,如果一開始先調用Write,默認會觸發(fā)WriteHeader(http.StatusOK)给郊,后續(xù)再調用WriteHeader就無效牡肉。
所以在開發(fā)中如果要測試非200的狀態(tài)時,需要先設置響應狀態(tài)碼WriteHeader(http.StatusBadRequest)淆九,然后再調用Write方法统锤。
我的博客: https://itart.cn/blogs/2021/practice/httptest-writeheader-invalid.html