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.