Gin has encapsulated some methods for constructing response, such as methods *Context.JSON(code int, obj interface{}) and *Context.String(code int, format string, values ...interface{}). These methods all call the method *Context.Render(code int, r render.Render).
// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
c.Status(code)
if !bodyAllowedForStatus(code) {
r.WriteContentType(c.Writer)
c.Writer.WriteHeaderNow()
return
}
if err := r.Render(c.Writer); err != nil {
panic(err)
}
}
I wonder why the Render method call the method Status which will set the HTTP response code by calling the method ResponseWriter.WriterHeader(statusCode int) firstly.
r.Render(c.Writer) will write Corresponding Content-Type to the response. Apparently it occurs after setting the status code (after calling method WriterHeader). According to the comment on method ResponseWriter.Header(), changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers. But setting Content-Type works in Gin.
func writeContentType(w http.ResponseWriter, value []string) {
header := w.Header()
if val := header["Content-Type"]; len(val) == 0 {
header["Content-Type"] = value
}
}
CodePudding user response:
c.Writer is a gin.ResponseWriter (probably the concrete type gin.responseWriter), not an http.ResponseWriter. While it implements the same interface, it doesn't do it in an identical way. Gin's WriteHeader doesn't send the headers immediately; it just stores the code internally in the writer, and WriteHeaderNow calls the "real" WriteHeader from net/http with the stored code.
WriteHeaderNow is called directly by the function you quoted in the case where there's no body; if there is a body, then WriteHeaderNow gets called on the first Write to the body.
