100

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

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

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

4

5 回答 5

79

你得到的w是一个指向非导出类型的指针,http.response但作为ResponseWriter一个接口,它是不可见的。

server.go

type ResponseWriter interface {
    ...
}

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

request.go

type Request struct {
    ...
}
于 2012-11-06T17:08:37.390 回答
35

http.ResponseWriter是一个接口,实现这个接口的现有类型是指针。这意味着不需要使用指向此接口的指针,因为它已经由指针“支持”。此处的一位 go 开发人员描述了这个概念虽然实现 http.ResponseWriter 的类型不需要是指针,但它并不实用,至少在 go http 服务器中不可行。

http.Request不是一个接口,它只是一个结构体,既然我们想改变这个结构体并让网络服务器看到这些变化,它必须是一个指针。如果它只是一个结构值,我们只需修改它的副本,调用我们的代码的 Web 服务器看不到它。

于 2012-11-06T17:33:57.940 回答
14

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

我想解决的是我认为这里存在的一个大而危险的误解,即请求通过“引用”传递的原因(尽管Go中并不真正存在这样的事情)是“我们想要做出改变它对服务器可见”。

引用几个答案:

[..] 它只是一个结构,因为我们想要更改这个结构并让 Web 服务器看到这些更改,所以它必须是一个指针 [..]所以

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

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

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

恰恰相反,不是吗?:-)

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

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

如果我们明确告诉人们不要改变请求,为什么还要使用指针?PerformanceRequest一个大型结构,复制它会降低性能,尤其是考虑到长中间件链。团队必须取得平衡,这绝对不是一个理想的解决方案,但这里的权衡显然是在性能方面(而不是 API 安全)。

于 2019-07-03T17:20:15.523 回答
2

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

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

也就是说,两个参数都是通过引用传入的;只是方法签名采用了一个 ResponseWriter,它是一个指向结构的指针的接口,所以它看起来好像是按值传递的。

于 2018-02-08T06:38:01.820 回答
1

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

于 2016-11-20T18:17:36.907 回答