场景
最近笔者在研究web框架过程中,发现了一个响应类型的问题,困扰许久,原因就是设置了响应状态码后,然后设置响应类型为application/json
。在实际请求后,响应类型变成了text/plain; charset=utf-8
格式。
问题解决:
先设置请求头的Content-type属性,再设置响应状态码,即可解决问题
// 例如: func writeContentType(w http.ResponseWriter) { header := w.Header() if val := header["Content-Type"]; len(val) == 0 { header["Content-Type"] = []string{"application/json; charset=utf-8"} } } // 先执行 writeContentType(w) // 再执行 w.WriteHeader(code)
分析导致问题的原因
我们处理请求接收的参数是:http.ResponseWriter
类型的,它是一个接口类型,只要实现了这个接口都可以作为参数传递进来。
而实际传递进来的是:response
结构体,它实现了http.ResponseWriter
接口
可以通过定位
http.ResponseWriter
结构体,在同文件里面找到response结构体
我们查看一下response
结构体的WriteHeader
方法的源码,里面有一段代码:
func (w *response) WriteHeader(code int) {
// 忽略代码
if w.calledHeader && w.cw.header == nil {
w.cw.header = w.handlerHeader.Clone()
}
// 忽略代码
}
再看一下response
结构体的Header
方法(因为我们实际就是调用它,然后设置响应头的):
func (w *response) Header() Header {
if w.cw.header == nil && w.wroteHeader && !w.cw.wroteHeader {
w.cw.header = w.handlerHeader.Clone()
}
w.calledHeader = true
return w.handlerHeader
}
总结问题:
通过分析上面两组代码可以发现,如果我们先执行了WriteHeader
方法,它会给w.cw.header
设置值,此时我们再调用Header
方法设置Content-type
属性时,经过if
判断,w.cw.header
并不等于nil
了,所以我们给header设置的属性无法设置到w.cw.header
上面,导致实际响应时,content-type
发生变化。关键点就在于w.cw.header
这个字段,如果设置的属性没到它上面的话,会导致失效。