Golang中HTTP:多余的Response.WriteHeader调用问题

Golang中HTTP:多余的Response.WriteHeader调用问题 在我的代码中,有一个循环用于处理一组文件(基于预指定文件夹中可用的文件),并根据每个处理文件的输出,将一些信息发送给客户端,因此我编写了以下代码:

	for i, file := range files {
		uniqueSlice := unique(matches)
		output = Output{MSG: "ok", File: file, Skills: uniqueSlice}
		data, err := json.Marshal(output)
		if err != nil {
			panic(err)
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK) // -< 错误来自这里
		w.Write(data)
	}

如果文件夹中只有一个文件,上述代码可以正常工作,但如果文件超过一个,我就会收到错误:http: superfluous response.WriteHeader call 我理解这个错误是由于使用了 w.WriteHeader(http.StatusOK),它不能被设置多次,但我需要为客户端设置它来处理返回的数据。 如果我移除 http.StatusOK,那么返回的数据将显示为纯文本而不是JSON!

如何修复这段代码,以便我可以在处理每个文件后直接将数据返回给客户端。


更多关于Golang中HTTP:多余的Response.WriteHeader调用问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

WriteHeader方法和SetHeader方法必须在Write方法之前调用,并且第一个for循环的Write方法在第二个for循环的WriteHeader方法之前。

更多关于Golang中HTTP:多余的Response.WriteHeader调用问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


无需设置状态为 http.StatusOK,因为这是默认值。同样,也无需显式设置传输编码为分块传输,因为这也是默认设置。

我建议直接创建一个 JSON 编码器,将其输出发送到 HTTP 响应,然后对每个输出对象调用它。

	w.Header().Set("Content-Type", "application/json")
	encoder := json.NewEncoder(w)
	for i, file := range files {
		uniqueSlice := unique(matches)
		output = Output{MSG: "ok", File: file, Skills: uniqueSlice}
		encoder.Encode(output)
	}

调用 w.Write 要求在之前设置 HTTP 状态码,否则会使用默认的 StatusOK。多次设置状态码会产生多余的日志。

你或许可以尝试使用 http.Flusher 实现分块传输编码。

package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"path"
)

func main() {
	http.HandleFunc("/files", streamFiles)
	err := http.ListenAndServe(":8090", nil)
	if err != nil {
		log.Fatal(err)
	}
}
func streamFiles(w http.ResponseWriter, r *http.Request) {
	files, err := ioutil.ReadDir("./files")
	if err != nil {
		log.Fatal(err)
	}
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.NotFound(w, r)
		return
	}

	w.Header().Set("Transfer-Encoding", "chunked")
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)

	for _, f := range files {
		buf, err := ioutil.ReadFile(path.Join("./files", f.Name()))
		if err != nil {
			log.Fatal(err)
		}
		w.Write(buf)
		flusher.Flush()
	}
}

https://play.golang.org/p/H2si4NdtmWa

问题在于你在循环中多次调用了 WriteHeader 方法。HTTP 响应头只能被设置一次,之后再次调用 WriteHeader 就会触发错误。

解决方案是在循环开始前设置一次响应头,然后在循环内只写入数据。以下是修正后的代码:

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) // 在循环外设置一次

for i, file := range files {
    uniqueSlice := unique(matches)
    output = Output{MSG: "ok", File: file, Skills: uniqueSlice}
    data, err := json.Marshal(output)
    if err != nil {
        panic(err)
    }
    w.Write(data)
    // 如果需要分隔多个JSON对象,可以写入换行符
    w.Write([]byte("\n"))
}

如果你需要为每个文件发送独立的 JSON 响应,可以考虑使用流式响应或分块传输编码。以下是使用 http.Flusher 的示例:

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)

flusher, ok := w.(http.Flusher)
if !ok {
    http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
    return
}

for i, file := range files {
    uniqueSlice := unique(matches)
    output = Output{MSG: "ok", File: file, Skills: uniqueSlice}
    data, err := json.Marshal(output)
    if err != nil {
        panic(err)
    }
    w.Write(data)
    w.Write([]byte("\n"))
    flusher.Flush() // 立即发送到客户端
}

如果每个文件处理都需要独立的 HTTP 响应,那么你需要重新设计接口,让客户端为每个文件发起单独的请求,或者使用 WebSocket 等双向通信协议。

回到顶部