在Go HTTP处理程序中,为什么ResponseWriter是一个值,而Request是一个指针?


82

我正在通过为GAE编写应用来学习Go,这是处理程序函数的签名:

func handle(w http.ResponseWriter, r *http.Request) {}

我在这里是指针新手,那么为什么Request对象是指针,但ResponseWriter不是呢?是否有必要采用这种方式,或者仅仅是为了使某种基于高级指针的代码成为可能?

Answers:


64

您得到的w是指向非导出类型的指针,http.response但与ResponseWriter接口一样,这是不可见的。

server.go

type ResponseWriter interface {
    ...
}

另一方面,r它是指向具体结构的指针,因此需要显式传递引用。

request.go

type Request struct {
    ...
}

28

http.ResponseWriter是一个接口,并实现此接口的现有类型的指针。这意味着无需使用指向该接口的指针,因为它已经由指针“支持”。这个概念是由go develpers的一个描述了一下这里虽然类型实现http.ResponseWriter并不需要是一个指针,它不实用,至少去http服务器之内。

http.Request不是一个接口,它只是一个结构,并且由于我们要更改此结构并使Web服务器看到这些更改,因此它必须是一个指针。如果只是一个struct值,我们将修改它的副本,这是调用我们的代码的Web服务器看不到的。


1
我认为这是不对的。指针后面的值可以实现与值相同的接口,因此此处无需区分。基本类型为int的类型可以满足接口,而无需成为指针或由其支持。
nemo 2012年

2
好吧,我并不是要暗示实现接口的所有东西都必须是指针。像ResponseWriter之类的东西,需要修改接口后面的值状态才能执行任何有用的操作,并且需要该值是指针类型(正如我链接到的博客文章所述)。但是可以,http.ResponseWriter理论上可以由int实现(其方法永远不能更改该int值)。
2012年

这个答案超级有帮助。该链接是无价的,为我们这些刚接触指针的人提供了清晰的解释。“就是说,尽管没有显式的标记,但接口对象通常像指针一样工作。在您了解真正的情况之前,这可能会造成混淆。”
Cody Django,

1
关于请求的部分实际上是错误的,请参阅我的答案 stackoverflow.com/a/56875204/989991
joakim

6

正如此处和其他地方的许多其他答案中正确提到的那样,它ResponseWriter是一个界面,其含义已在SO答案和博客中进行了详细描述。

我要解决的是我的感觉,这是一个巨大且危险的误解,原因是请求是通过“引用”传递的(尽管Go中并不存在这样的东西)是“我们想要进行更改使其对服务器可见”。

引用几个答案:

[..]它只是一个结构,因为我们想改变这个结构,并在Web服务器看到这些变化,它必须是一个指针[..] SO

[..]处理程序对Request的更改需要对服务器可见,因此我们仅通过引用而不是通过值来传递[..] SO

这是错误的; 实际上,文档明确警告不要篡改/更改请求

除读取正文外,处理程序不应修改提供的请求。

相反,不是吗?:-)

如果要更改请求,例如在将跟踪标头传递给中间件链中的下一个处理程序之前,添加一个跟踪标头,则必须复制该请求并将复制的版本向下传递给链。

Go团队提出了更改行为以允许修改传入请求的请求,但是更改此类内容可能会导致至少某些现有代码意外中断。

如果我们明确告诉人们不要改变请求,为什么还要使用指针?性能Request是一个大的结构,并复制它可以带来性能的下降,尤其是在考虑长期中间件链。团队必须权衡利弊,这绝对不是理想的解决方案,但是这里的权衡显然是性能方面的问题(而不是API安全性)。


这是最终对我有意义的答案。
Jimi

1

它是指向Request的指针的原因很简单:处理程序对Request的更改需要对服务器可见,因此我们仅按引用而不是按值传递它。

如果深入研究net / http库代码,您会发现ResponseWriter是未导出结构响应的接口,并且我们通过引用传递结构(我们传递指向响应的指针)而不是值。ResponseWriter是处理程序用来创建HTTP响应的接口。备份ResponseWriter的实际结构是未导出的结构http.response。因为它是非导出的,所以不能直接使用它。您只能通过ResponseWriter界面使用它。

换句话说,这两个参数都是通过引用传递的。只是方法签名采用了一个ResponseWriter,它是指向结构指针的接口,因此看起来好像是按值传递的。


使得比原来的答案,我更有意义
文卡塔SSKM切塔尼亚

关于请求的部分实际上是错误的,请参阅我的答案stackoverflow.com/a/56875204/989991
joakim

0

我认为将Request对象作为指针传递的主要原因是Body字段。对于给定的HTTP请求,正文只能读取一次。如果Request克隆了对象(就像未将其作为指针传递一样),那么我们将有两个对象,它们具有有关从主体读取多少信息的不同信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.