简体   繁体   English

多个response.WriteHeader调用真的很简单的例子?

[英]multiple response.WriteHeader calls in really simple example?

I have the most basic net/http program that I'm using to learn the namespace in Go: 我有最基本的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!"))
}

When I run the program and connect to localhost:5678 in Chrome, I get this in the console: 当我运行程序并连接到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

But I don't see how that's possible. 但我不明白这是怎么可能的。 I print the URL, start up a new goroutine, write the header once, and give it a static body of Hello, World! 我打印URL,启动一个新的goroutine,写一次标题,并给它一个静态的Hello, World! It seems like one of two things is happening. 似乎正在发生两件事之一。 Either something behind the scenes is writing another header or somehow HandleIndex is called twice for the same request. 幕后的任何东西都在写另一个标题,或者以某种方式为同一个请求调用HandleIndex两次。 What can I do to stop writing multiple headers? 我该怎么做才能停止写多个标题?

EDIT: It seems to have something to do with the go HandleIndex(w, r) line because if I remove go and just make it a function call instead of a goroutine, I don't get any issues and the browser gets it's data. 编辑:它似乎与go HandleIndex(w, r)行有关,因为如果我删除go并且只是使它成为函数调用而不是goroutine,我不会遇到任何问题并且浏览器获取它的数据。 With it being a goroutine, I get the multiple WriteHeader error and the browser doesn't show "Hello World." 由于它是goroutine,我得到多个WriteHeader错误,浏览器不显示“Hello World”。 Why is making this a goroutine breaking it? 为什么要把这个打破呢?

Take a look at the anonymous function you register as the handler of incoming requests: 看一下您注册为传入请求处理程序的匿名函数:

func(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.URL)
    go HandleIndex(w, r)
}

It prints the URL (to the standard output) then calls HandleIndex() in a new goroutine and continues execution. 它打印URL(到标准输出),然后在新的goroutine中调用HandleIndex()并继续执行。

If you have a handler function where you do not set the response status before the first call to Write , Go will automatically set the response status to 200 (HTTP OK). 如果您有一个处理程序函数,在第一次调用Write之前没有设置响应状态,Go将自动将响应状态设置为200(HTTP OK)。 If the handler function does not write anything to the response (and does not set the response status and completes normally), that is also treated as a successful handling of the request and the response status 200 will be sent back. 如果处理程序函数没有向响应写入任何内容(并且没有设置响应状态并且正常完成),那么这也被视为对请求的成功处理,并且将发回响应状态200。 Your anonymous function does not set it, it does not even write anything to the response. 你的匿名函数没有设置它,它甚至没有写任何响应。 So Go will do just that: set the response status to 200 HTTP OK. 所以Go会这样做:将响应状态设置为200 HTTP OK。

Note that handling each request runs in its own goroutine. 请注意,处理每个请求都在其自己的goroutine中运行。

So if you call HandleIndex in a new goroutine, your original anonymous function will continue: it will end and so the response header will be set - meanwhile (concurrently) your started new goroutine will also set the response header - hence the "multiple response.WriteHeader calls" error. 因此,如果你在一个新的goroutine中调用HandleIndex ,你的原始匿名函数将继续:它将结束,因此响应头将被设置 - 同时(同时)你的启动新goroutine也将设置响应头 - 因此"multiple response.WriteHeader calls"错误。

If you remove the "go" , your HandleIndex function will set the response header in the same goroutine before your handler function returns, and the "net/http" will know about this and will not try to set the response header again, so the error you experienced will not happen. 如果你删除"go" ,你的HandleIndex函数会在你的处理函数返回之前在同一个goroutine中设置响应头,并且“net / http”会知道这个,并且不会再尝试设置响应头,所以你遇到的错误不会发生。

You already received a correct answer which addresses your problem, I will give some information about the general case (such error appears often). 您已经收到了解决问题的正确答案,我将提供有关一般情况的一些信息(这种错误经常出现)。

From the documentation , you see that WriteHeader sends an http status code and you can't send more than 1 status code. 文档中 ,您可以看到WriteHeader发送http状态代码,并且您不能发送超过1个状态代码。 If you Write anything this is equivalent to sending 200 status code and then writing things. 如果你Write任何东西,这相当于发送200状态代码然后写东西。

So the message that you see appears if you either user w.WriteHeader more than once explicitly or uses w.Write before w.WriteHeader . 所以,你看到的信息出现,如果你或者用户w.WriteHeader不止一次明确地或使用w.Write之前w.WriteHeader

From the documentation: 从文档:

// 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).

What is happening in your case is that you are launching go HandleIndex from the handler. 在你的情况下发生的是你从处理程序启动go HandleIndex The first handler finishes. 第一个处理程序完成。 The standard WriteHeader writes to the ResponseWriter. 标准WriteHeader写入ResponseWriter。 Then the go routine HandleIndex is launched and it also tries to write a header and write. 然后启动go例程HandleIndex,它还尝试编写标题并写入。

Just remove the go from HandleIndex and it will work. 只需卸下go从HandleIndex,它会工作。

the root cause is that you called WriteHeader more than once. 根本原因是您不止一次调用WriteHeader。 from the source codes 来自源代码

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")
        }
    }
}

so when you wrote once, the variable wroteHeader would be true, then you wrote header again, it wouldn't be effective and gave a warning "http: multiple respnse.WriteHeader calls". 所以当你写一次,变量writeHeader将是真的,然后你再次写入标题,它将无效并发出警告“http:multiple respnse.WriteHeader calls”。 actually the function Write also calls WriteHeader, so putting the function WriteHeader after the function Write also causes that error, and the later WriteHeader doesn't work. 实际上函数Write也调用WriteHeader,因此在函数Write之后放置函数WriteHeader也会导致该错误,后来的WriteHeader不起作用。

from your case, go handleindex runs in another thread and the original already returns, if you do nothing, it will call WriteHeader to set 200. when running handleindex, it calls another WriteHeader, at that time wroteHeader is true, then the message "http: multiple response.WriteHeader calls" is output. 从你的情况来看,go handleindex在另一个线程中运行并且原来已经返回,如果你什么都不做,它会调用WriteHeader设置200.当运行handleindex时,它调用另一个WriteHeader,当时writeHeader为true,然后消息“http :输出多个response.WriteHeader调用“。

Yes, use HandleIndex(w, r) instead of go HandleIndex(w, r) will fix your issue, I think you have already figured that out. 是的,使用HandleIndex(w, r)而不是go HandleIndex(w, r)将解决你的问题,我想你已经想到了这一点。

The reason is simple, when handling multiple requests at the same time, the http server will start multiple goroutines, and your handler function will be called separately in each of the goroutines without blocking others. 原因很简单,当同时处理多个请求时,http服务器将启动多个goroutine,并且您的处理函数将在每个goroutine中单独调用,而不会阻塞其他函数。 You don't need to start your own goroutine in the handler, unless you practically need it, but that will be another topic. 您不需要在处理程序中启动自己的goroutine,除非您实际需要它,但这将是另一个主题。

Because modern browsers send an extra request for /favicon.ico which is also handled in your / request handler. 因为现代浏览器会发送额外的/favicon.ico请求,这也是在/ request处理程序中处理的。

If you ping your server with curl for example, you'll see only one request being sent: 例如,如果使用curl ping服务器,则只会看到一个请求被发送:

 curl localhost:5678

To be sure you can add an EndPoint in your http.HandleFunc 确保您可以在http.HandleFunc中添加EndPoint

http.HandleFunc("/Home", func(w http.ResponseWriter, r *http.Request) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM