场景
最近笔者在研究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 = truereturn w.handlerHeader
}
总结问题:
通过分析上面两组代码可以发现,如果我们先执行了WriteHeader方法,它会给w.cw.header设置值,此时我们再调用Header方法设置Content-type属性时,经过if判断,w.cw.header并不等于nil了,所以我们给header设置的属性无法设置到w.cw.header上面,导致实际响应时,content-type发生变化。关键点就在于w.cw.header这个字段,如果设置的属性没到它上面的话,会导致失效。