Why response code is set firstly in method *Context.Render(code int, r render.Render) of gin?

huangapple go评论76阅读模式
英文:

Why response code is set firstly in method *Context.Render(code int, r render.Render) of gin?

问题

Gin封装了一些用于构建响应的方法,例如*Context.JSON(code int, obj interface{})*Context.String(code int, format string, values ...interface{})。这些方法都会调用*Context.Render(code int, r render.Render)方法。

// Render写入响应头并调用render.Render来渲染数据。
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)
	}
}

我想知道为什么Render方法会调用Status方法,而Status方法会首先调用ResponseWriter.WriterHeader(statusCode int)方法来设置HTTP响应代码。

r.Render(c.Writer)将会向响应中写入相应的Content-Type。显然,这发生在设置状态码之后(在调用WriterHeader方法之后)。根据ResponseWriter.Header()方法的注释,在调用WriteHeader(或Write)之后修改头部映射是没有效果的,除非修改的头部是尾部。但是在Gin中设置Content-Type是有效的。

func writeContentType(w http.ResponseWriter, value []string) {
	header := w.Header()
	if val := header["Content-Type"]; len(val) == 0 {
		header["Content-Type"] = value
	}
}
英文:

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
	}
}

答案1

得分: 2

c.Writer 是一个 gin.ResponseWriter(可能是具体类型 gin.responseWriter),而不是 http.ResponseWriter。虽然它实现了相同的接口,但它的实现方式并不完全相同。Gin 的 WriteHeader 并不会立即发送头部信息,它只是将 code 内部存储在 writer 中,而 WriteHeaderNow 方法会使用存储的 code 调用 net/http 中的真正的 WriteHeader 方法。

在你引用的函数中,如果没有响应体,则直接调用 WriteHeaderNow 方法;如果有响应体,则在第一次对响应体进行写入时调用 WriteHeaderNow

英文:

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.

huangapple
  • 本文由 发表于 2021年9月13日 11:10:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/69156843.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定