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

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

In Go HTTP handlers, why is the ResponseWriter a value but the Request a pointer?

问题

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

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

我在这里是一个指针新手,所以为什么Request对象是一个指针,而ResponseWriter不是呢?有必要这样做吗,还是只是为了使某种高级指针代码成为可能?

英文:

I'm learning Go by writing an app for GAE, and this is signature of a handler function:

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

I'm pointer newbie here, so why is the Request object a pointer, but the ResponseWriter not? Is there any need to have it this way or is this just to make some kind of advanced pointer based code possible?

答案1

得分: 90

你得到的w是一个指向未导出类型http.response的指针,但是由于ResponseWriter是一个接口,所以它是不可见的。

server.go中可以看到:

type ResponseWriter interface {
    ...
}

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

request.go中可以看到:

type Request struct {
    ...
}
英文:

What you get for w is a pointer to the non exported type http.response but as ResponseWriter is an interface, that's not visible.

From server.go:

type ResponseWriter interface {
    ...
}

On the other hand, r is a pointer to a concrete struct, hence the need to pass a reference explicitly.

From request.go:

type Request struct {
    ...
}

答案2

得分: 37

http.ResponseWriter 是一个接口,实现这个接口的现有类型是指针。这意味着不需要使用指针来引用这个接口,因为它已经由一个指针“支持”。这个概念在这里由Go开发者之一进行了一些描述。虽然实现http.ResponseWriter的类型不需要是指针,但至少在Go的HTTP服务器中并不实用。

http.Request 不是一个接口,它只是一个结构体,由于我们想要修改这个结构体并让Web服务器看到这些变化,所以它必须是一个指针。如果它只是一个结构体值,我们只会修改一个Web服务器调用我们代码时看不到的副本。

英文:

The http.ResponseWriter is an interface, and the existing types implementing this interface are pointers. That means there's no need to use a pointer to this interface, as it's already "backed" by a pointer. This concept is describe a bit by one of the go develpers here Although a type implementing http.ResponseWriter didn't need to be a pointer, it would not be practical, at least not within the go http server.

http.Request is not an interface, it's just a struct, and since we want to change this struct and have the web server see those changes, it has to be a pointer. If it was just a struct value, we would just modify a copy of it that the web server calling our code could not see.

答案3

得分: 24

正如其他答案和博客中正确提到的那样,ResponseWriter是一个接口,这个接口的含义在SO的答案和博客中已经详细描述过。

我想要解释的是这里存在的一个大误解,即请求被传递为“引用”(尽管在Go中实际上并不存在这样的东西)的原因是“我们希望对其进行的更改对服务器可见”。

引用几个答案:

[..] 它只是一个结构体,由于我们希望更改此结构体并使Web服务器看到这些更改,所以它必须是一个指针 [..] SO

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

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

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

恰恰相反,不是吗? 在Go的HTTP处理程序中,为什么ResponseWriter是一个值,而Request是一个指针?

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

已经提出了更改允许修改传入请求行为的请求(链接),但更改这样的东西可能会导致一些现有代码意外中断。

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

英文:

As correctly mentioned in many other answers here and elsewhere, ResponseWriter is an interface and the implications of this have been described in detail in SO answers and blogs.

What I would like to address is what I feel is the big—and dangerous—misconception here, that the reason request is passed by "reference" (although such a thing does not really exist in Go) is that "we want to make changes to it visible to the server".

Quoting a couple of answers:

> [..] it's just a struct, and since we want to change this struct and have the web server see those changes, it has to be a pointer [..] SO
>
> [..] changes to Request by the handler need to be visible to the server, so we’re only passing it by reference instead of by value [..] SO

This is wrong; in fact the docs explicitly warn against tampering with / mutating the request:

> Except for reading the body, handlers should not modify the provided Request.

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

If you want to change the request, e.g. append a tracing header before passing it on to the next handler in a middleware chain you have to copy the request and pass the copied version down the chain.

Requests to change the behavior to allow modifications of the incoming request have been raised with the Go team but changing something like this would probably lead to at least some existing code breaking unexpectedly.

Why use a pointer if we are explicitly telling people not to mutate the request? Performance, Request is a large struct and copying it can bring performance down, especially with long middleware chains in mind. The team had to strike a balance, definitely not an ideal solution, but the tradeoffs are clearly on the side of performance here (instead of API safety).

答案4

得分: 2

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

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

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

英文:

The reason why it’s a pointer to Request is simple: changes to Request by the handler need to be visible to the server, so we’re only passing it by reference instead of by value.

If you dig into the net/http library code, you’ll find that ResponseWriter is an interface to a nonexported struct response, and we’re passing the struct by reference (we’re passing in a pointer to response) and not by value. ResponseWriter is an interface that a handler uses to create an HTTP response. The actual struct backing up ResponseWriter is the nonexported struct http.response. Because it’s nonexported, you can’t use it directly; you can only use it through the ResponseWriter interface.

In other words, both the parameters are passed in by reference; it’s just that the method signature takes a ResponseWriter that’s an interface to a pointer to a struct, so it looks as if it’s passed in by value.

答案5

得分: 1

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

英文:

I think that the main reason for the Request object to be passed as a pointer is the Body field. For a given HTTP request, the body can only we read once. If the Request object was cloned, as it would be if it wasn't passed as a pointer, we would have two objects with different information about how much has been read from the body.

huangapple
  • 本文由 发表于 2012年11月7日 01:07:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/13255907.html
匿名

发表评论

匿名网友

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

确定