Несколько ответов .WriteHeader вызывает действительно простой пример?
У меня есть самая простая программа 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!"))
}
Когда я запускаю программу и подключаюсь к localhost:5678
в Chrome, я получаю ее в консоли:
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, пишу заголовок один раз и даю ему статическое тело Hello, World!
Кажется, что происходит одна из двух вещей. Либо что-то за кулисами пишет другой заголовок, либо как-то HandleIndex
вызывается дважды для одного и того же запроса. Что я могу сделать, чтобы прекратить писать несколько заголовков?
EDIT: похоже, что-то связано с линией go HandleIndex(w, r)
, потому что если я удалю go
и просто сделаю это вызовом функции вместо goroutine, я не получу никаких проблем, и браузер получит данные, Поскольку он является goroutine, я получаю множественную ошибку WriteHeader, и браузер не показывает "Hello World". Почему это нарушает его голотин?
Ответы
Ответ 1
Взгляните на анонимную функцию, которую вы регистрируете как обработчик входящих запросов:
func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
go HandleIndex(w, r)
}
Он печатает URL (на стандартный вывод), затем вызывает HandleIndex()
в новом goroutine и продолжает выполнение.
Если у вас есть функция обработчика, в которой вы не задали статус ответа до первого вызова Write
, Go автоматически установит статус ответа на 200 (HTTP OK). Если функция обработчика ничего не записывает в ответ (и не устанавливает статус ответа и завершается нормально), это также рассматривается как успешная обработка запроса, и ответный статус 200 будет отправлен обратно. Ваша анонимная функция не устанавливает его, он даже не пишет ничего ответа. Итак, Go будет делать именно это: установите статус ответа на 200 HTTP OK.
Обратите внимание, что обработка каждого запроса выполняется в своем собственном goroutine.
Итак, если вы вызываете HandleIndex
в новом goroutine, ваша исходная анонимная функция будет продолжена: она закончится, и поэтому заголовок ответа будет установлен - между тем (одновременно) ваш запущенный новый goroutine также установит заголовок ответа - следовательно ошибка "multiple response.WriteHeader calls"
.
Если вы удалите "go"
, ваша функция HandleIndex
установит заголовок ответа в том же goroutine перед возвратом функции обработчика, и "net/http" узнает об этом и не будет пытаться установить ответ заголовок снова, поэтому ошибка, с которой вы столкнулись, не произойдет.
Ответ 2
Поскольку современные браузеры отправляют дополнительный запрос для /favicon.ico, который также обрабатывается в вашем обработчике /request.
Если вы ping ваш сервер с завиткой, например, вы увидите только один отправляемый запрос:
curl localhost:5678
Чтобы убедиться, что вы можете добавить EndPoint в свой http.HandleFunc
http.HandleFunc("/Home", func(w http.ResponseWriter, r *http.Request)
Ответ 3
Вы уже получили правильный ответ, который касается вашей проблемы, я дам некоторую информацию об общем случае (такая ошибка появляется часто).
Из документации вы видите, что WriteHeader
отправляет код состояния http, и вы не можете отправить более 1 кода состояния. Если вы Write
что-либо, это эквивалентно отправке кода состояния 200, а затем записи вещей.
Таким образом, появившееся сообщение появляется, если вы либо пользователь w.WriteHeader
более одного раза явно, либо использует w.Write
до w.WriteHeader
.
Ответ 4
Из документации:
// 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. Затем запускается рутинная ручка HandleIndex, которая также пытается записать заголовок и записать.
Просто удалите go
из HandleIndex, и он будет работать.
Ответ 5
Да, используйте HandleIndex(w, r)
вместо go HandleIndex(w, r)
, чтобы исправить вашу проблему, я думаю, вы уже поняли это.
Причина проста: при одновременном обработке нескольких запросов сервер http запускает несколько goroutines, и ваша функция обработчика будет вызываться отдельно в каждом из goroutines, не блокируя других.
Вам не нужно запускать свой собственный goroutine в обработчике, если вам это практически не нужно, но это будет другая тема.
Ответ 6
Основная причина заключается в том, что вы вызывали 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")
}
}
}
поэтому, когда вы написали один раз, переменная writHeader была бы правдой, тогда вы снова написали заголовок, это было бы неэффективно и дало бы предупреждение "http: multiple respnse.WriteHeader calls".
на самом деле функция Write также вызывает WriteHeader, поэтому ставьте функцию WriteHeader после того, как функция Write также вызывает эту ошибку, а более поздняя запись WriteHeader не работает.
из вашего случая, go handleindex работает в другом потоке, и исходный файл уже возвращается, если вы ничего не сделаете, он вызовет WriteHeader для установки 200. При запуске handleindex он вызывает другой WriteHeader, в то время writeHeader является истинным, тогда появляется сообщение "http: multiple response.WriteHeader calls".