我正在通过为GAE编写应用来学习Go,这是处理程序函数的签名:
func handle(w http.ResponseWriter, r *http.Request) {}
我在这里是指针新手,那么为什么Request
对象是指针,但ResponseWriter
不是呢?是否有必要采用这种方式,或者仅仅是为了使某种基于高级指针的代码成为可能?
我正在通过为GAE编写应用来学习Go,这是处理程序函数的签名:
func handle(w http.ResponseWriter, r *http.Request) {}
我在这里是指针新手,那么为什么Request
对象是指针,但ResponseWriter
不是呢?是否有必要采用这种方式,或者仅仅是为了使某种基于高级指针的代码成为可能?
Answers:
您得到的w
是指向非导出类型的指针,http.response
但与ResponseWriter
接口一样,这是不可见的。
type ResponseWriter interface {
...
}
另一方面,r
它是指向具体结构的指针,因此需要显式传递引用。
type Request struct {
...
}
该http.ResponseWriter
是一个接口,并实现此接口的现有类型的指针。这意味着无需使用指向该接口的指针,因为它已经由指针“支持”。这个概念是由go develpers的一个描述了一下这里虽然类型实现http.ResponseWriter并不需要是一个指针,它不实用,至少去http服务器之内。
http.Request
不是一个接口,它只是一个结构,并且由于我们要更改此结构并使Web服务器看到这些更改,因此它必须是一个指针。如果只是一个struct值,我们将修改它的副本,这是调用我们的代码的Web服务器看不到的。
正如此处和其他地方的许多其他答案中正确提到的那样,它ResponseWriter
是一个界面,其含义已在SO答案和博客中进行了详细描述。
我要解决的是我的感觉,这是一个巨大且危险的误解,原因是请求是通过“引用”传递的(尽管Go中并不存在这样的东西)是“我们想要进行更改使其对服务器可见”。
引用几个答案:
[..]它只是一个结构,因为我们想改变这个结构,并在Web服务器看到这些变化,它必须是一个指针[..] SO
[..]处理程序对Request的更改需要对服务器可见,因此我们仅通过引用而不是通过值来传递[..] SO
这是错误的; 实际上,文档明确警告不要篡改/更改请求:
除读取正文外,处理程序不应修改提供的请求。
相反,不是吗?:-)
如果要更改请求,例如在将跟踪标头传递给中间件链中的下一个处理程序之前,添加一个跟踪标头,则必须复制该请求并将复制的版本向下传递给链。
Go团队提出了更改行为以允许修改传入请求的请求,但是更改此类内容可能会导致至少某些现有代码意外中断。
如果我们明确告诉人们不要改变请求,为什么还要使用指针?性能,Request
是一个大的结构,并复制它可以带来性能的下降,尤其是在考虑长期中间件链。团队必须权衡利弊,这绝对不是理想的解决方案,但是这里的权衡显然是性能方面的问题(而不是API安全性)。
它是指向Request的指针的原因很简单:处理程序对Request的更改需要对服务器可见,因此我们仅按引用而不是按值传递它。
如果深入研究net / http库代码,您会发现ResponseWriter是未导出结构响应的接口,并且我们通过引用传递结构(我们传递指向响应的指针)而不是值。ResponseWriter是处理程序用来创建HTTP响应的接口。备份ResponseWriter的实际结构是未导出的结构http.response。因为它是非导出的,所以不能直接使用它。您只能通过ResponseWriter界面使用它。
换句话说,这两个参数都是通过引用传递的。只是方法签名采用了一个ResponseWriter,它是指向结构指针的接口,因此看起来好像是按值传递的。