在非常简单的示例中多次 response.WriteHeader 调用?
multiple response.WriteHeader calls in really simple example?
我有最基本的 net/http 程序,我用它来学习 Go 中的命名空间:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
go HandleIndex(w, r)
})
fmt.Println("Starting Server...")
log.Fatal(http.ListenAndServe(":5678", nil))
}
func HandleIndex(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("Hello, World!"))
}
当我 运行 程序并连接到 Chrome 中的 localhost:5678
时,我在控制台中得到了这个:
Starting Server...
/
2015/01/15 13:41:29 http: multiple response.WriteHeader calls
/favicon.ico
2015/01/15 13:41:29 http: multiple response.WriteHeader calls
但我不明白这怎么可能。我打印 URL,启动一个新的 goroutine,写一次 header,然后给它一个 Hello, World!
的静态 body 似乎正在发生两件事之一。幕后的某些东西正在写另一个 header 或不知何故 HandleIndex
被同一个请求调用两次。我该怎么做才能停止写入多个 headers?
编辑:它似乎与 go HandleIndex(w, r)
行有关,因为如果我删除 go
并使其成为函数调用而不是 goroutine,我不会遇到任何问题浏览器获取它的数据。由于它是一个 goroutine,我收到多个 WriteHeader 错误并且浏览器不显示 "Hello World." 为什么将其设为 goroutine 会破坏它?
来自文档:
// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
您的情况是您从处理程序启动 go HandleIndex
。
第一个处理程序完成。标准 WriteHeader 写入 ResponseWriter。然后 go routine HandleIndex 被启动,它也尝试写一个 header 并写。
只需从 HandleIndex 中删除 go
即可。
看看您注册为传入请求处理程序的匿名函数:
func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
go HandleIndex(w, r)
}
它打印 URL(到标准输出)然后在新的 goroutine 中调用 HandleIndex()
并继续执行。
如果您有一个处理函数,在第一次调用 Write
之前没有设置响应状态,Go 会自动将响应状态设置为 200(HTTP OK)。如果处理函数没有向响应写入任何内容(并且没有设置响应状态并正常完成),这也被视为成功处理了请求,响应状态 200 将被发回。您的匿名函数不会设置它,它甚至不会向响应写入任何内容。所以 Go 会这样做:将响应状态设置为 200 HTTP OK。
请注意,处理每个请求都在其自己的 goroutine 中运行。
因此,如果您在新的 goroutine 中调用 HandleIndex
,您原来的匿名函数将继续:它将结束,因此将设置响应 header - 同时(同时)您启动的新 goroutine还将设置响应 header - 因此 "multiple response.WriteHeader calls"
错误。
如果您删除 "go"
,您的 HandleIndex
函数将在处理函数 returns 和 [=29] 之前的同一个 goroutine 中设置响应 header =] 将知道这一点并且不会尝试再次设置响应 header,因此不会发生您遇到的错误。
是的,使用 HandleIndex(w, r)
而不是 go HandleIndex(w, r)
将解决您的问题,我想您已经明白了。
原因很简单,当同时处理多个请求时,http服务器会启动多个goroutines,你的handler函数会在每个goroutines中分别调用,不会阻塞其他goroutines。
你不需要在处理程序中启动你自己的 goroutine,除非你实际需要它,但这将是另一个话题。
因为现代浏览器会发送额外的 /favicon.ico 请求,该请求也会在您的 / 请求处理程序中处理。
例如,如果您使用 curl ping 服务器,您将看到只发送了一个请求:
curl localhost:5678
确保您可以在 http.HandleFunc
中添加端点
http.HandleFunc("/Home", func(w http.ResponseWriter, r *http.Request)
您已经收到了解决您问题的正确答案,我将提供一些有关一般情况的信息(这种错误经常出现)。
从 documentation 中,您看到 WriteHeader
发送了一个 http 状态代码,您不能发送超过 1 个状态代码。如果你 Write
任何这相当于发送 200 状态代码然后写东西。
因此,如果您多次明确使用 w.WriteHeader
或在 w.WriteHeader
之前使用 w.Write
,就会出现您看到的消息。
根本原因是你不止一次调用了WriteHeader。来自源代码
func (w *response) WriteHeader(code int) {
if w.conn.hijacked() {
w.conn.server.logf("http: response.WriteHeader on hijacked connection")
return
}
if w.wroteHeader {
w.conn.server.logf("http: multiple response.WriteHeader calls")
return
}
w.wroteHeader = true
w.status = code
if w.calledHeader && w.cw.header == nil {
w.cw.header = w.handlerHeader.clone()
}
if cl := w.handlerHeader.get("Content-Length"); cl != "" {
v, err := strconv.ParseInt(cl, 10, 64)
if err == nil && v >= 0 {
w.contentLength = v
} else {
w.conn.server.logf("http: invalid Content-Length of %q", cl)
w.handlerHeader.Del("Content-Length")
}
}
}
所以你写了一次,变量writeHeader就为真,然后你再写header,就失效了,警告了"http: multiple respnse.WriteHeader calls"。
实际上Write函数也调用了WriteHeader,所以把WriteHeader函数放在Write函数后面也会导致那个错误,后面的WriteHeader不起作用。
从你的情况来看,go handleindex在另一个线程中运行,原来的已经returns,如果你什么都不做,它会调用WriteHeader设置200。当运行 handleindex时,它会调用另一个WriteHeader ,此时writeHeader为true,则输出消息"http: multiple response.WriteHeader calls".
我有最基本的 net/http 程序,我用它来学习 Go 中的命名空间:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
go HandleIndex(w, r)
})
fmt.Println("Starting Server...")
log.Fatal(http.ListenAndServe(":5678", nil))
}
func HandleIndex(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("Hello, World!"))
}
当我 运行 程序并连接到 Chrome 中的 localhost:5678
时,我在控制台中得到了这个:
Starting Server...
/
2015/01/15 13:41:29 http: multiple response.WriteHeader calls
/favicon.ico
2015/01/15 13:41:29 http: multiple response.WriteHeader calls
但我不明白这怎么可能。我打印 URL,启动一个新的 goroutine,写一次 header,然后给它一个 Hello, World!
的静态 body 似乎正在发生两件事之一。幕后的某些东西正在写另一个 header 或不知何故 HandleIndex
被同一个请求调用两次。我该怎么做才能停止写入多个 headers?
编辑:它似乎与 go HandleIndex(w, r)
行有关,因为如果我删除 go
并使其成为函数调用而不是 goroutine,我不会遇到任何问题浏览器获取它的数据。由于它是一个 goroutine,我收到多个 WriteHeader 错误并且浏览器不显示 "Hello World." 为什么将其设为 goroutine 会破坏它?
来自文档:
// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
您的情况是您从处理程序启动 go HandleIndex
。
第一个处理程序完成。标准 WriteHeader 写入 ResponseWriter。然后 go routine HandleIndex 被启动,它也尝试写一个 header 并写。
只需从 HandleIndex 中删除 go
即可。
看看您注册为传入请求处理程序的匿名函数:
func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
go HandleIndex(w, r)
}
它打印 URL(到标准输出)然后在新的 goroutine 中调用 HandleIndex()
并继续执行。
如果您有一个处理函数,在第一次调用 Write
之前没有设置响应状态,Go 会自动将响应状态设置为 200(HTTP OK)。如果处理函数没有向响应写入任何内容(并且没有设置响应状态并正常完成),这也被视为成功处理了请求,响应状态 200 将被发回。您的匿名函数不会设置它,它甚至不会向响应写入任何内容。所以 Go 会这样做:将响应状态设置为 200 HTTP OK。
请注意,处理每个请求都在其自己的 goroutine 中运行。
因此,如果您在新的 goroutine 中调用 HandleIndex
,您原来的匿名函数将继续:它将结束,因此将设置响应 header - 同时(同时)您启动的新 goroutine还将设置响应 header - 因此 "multiple response.WriteHeader calls"
错误。
如果您删除 "go"
,您的 HandleIndex
函数将在处理函数 returns 和 [=29] 之前的同一个 goroutine 中设置响应 header =] 将知道这一点并且不会尝试再次设置响应 header,因此不会发生您遇到的错误。
是的,使用 HandleIndex(w, r)
而不是 go HandleIndex(w, r)
将解决您的问题,我想您已经明白了。
原因很简单,当同时处理多个请求时,http服务器会启动多个goroutines,你的handler函数会在每个goroutines中分别调用,不会阻塞其他goroutines。 你不需要在处理程序中启动你自己的 goroutine,除非你实际需要它,但这将是另一个话题。
因为现代浏览器会发送额外的 /favicon.ico 请求,该请求也会在您的 / 请求处理程序中处理。
例如,如果您使用 curl ping 服务器,您将看到只发送了一个请求:
curl localhost:5678
确保您可以在 http.HandleFunc
中添加端点http.HandleFunc("/Home", func(w http.ResponseWriter, r *http.Request)
您已经收到了解决您问题的正确答案,我将提供一些有关一般情况的信息(这种错误经常出现)。
从 documentation 中,您看到 WriteHeader
发送了一个 http 状态代码,您不能发送超过 1 个状态代码。如果你 Write
任何这相当于发送 200 状态代码然后写东西。
因此,如果您多次明确使用 w.WriteHeader
或在 w.WriteHeader
之前使用 w.Write
,就会出现您看到的消息。
根本原因是你不止一次调用了WriteHeader。来自源代码
func (w *response) WriteHeader(code int) {
if w.conn.hijacked() {
w.conn.server.logf("http: response.WriteHeader on hijacked connection")
return
}
if w.wroteHeader {
w.conn.server.logf("http: multiple response.WriteHeader calls")
return
}
w.wroteHeader = true
w.status = code
if w.calledHeader && w.cw.header == nil {
w.cw.header = w.handlerHeader.clone()
}
if cl := w.handlerHeader.get("Content-Length"); cl != "" {
v, err := strconv.ParseInt(cl, 10, 64)
if err == nil && v >= 0 {
w.contentLength = v
} else {
w.conn.server.logf("http: invalid Content-Length of %q", cl)
w.handlerHeader.Del("Content-Length")
}
}
}
所以你写了一次,变量writeHeader就为真,然后你再写header,就失效了,警告了"http: multiple respnse.WriteHeader calls"。 实际上Write函数也调用了WriteHeader,所以把WriteHeader函数放在Write函数后面也会导致那个错误,后面的WriteHeader不起作用。
从你的情况来看,go handleindex在另一个线程中运行,原来的已经returns,如果你什么都不做,它会调用WriteHeader设置200。当运行 handleindex时,它会调用另一个WriteHeader ,此时writeHeader为true,则输出消息"http: multiple response.WriteHeader calls".