Golang WriteHeader设置后,Content-Type失效的问题

场景

最近笔者在编写web框架过程中,发现了一个响应类型的问题,困扰许久,原因就是设置了响应状态码后,然后设置响应类型为application/json。在实际请求后,响应类型变成了text/plain; charset=utf-8格式。

Golang WriteHeader设置后,Content-Type失效的问题_第1张图片
Golang WriteHeader设置后,Content-Type失效的问题_第2张图片

问题解决:

先设置请求头的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这个字段,如果设置的属性没到它上面的话,会导致失效。

你可能感兴趣的:(随笔,golang,http)