Http: superfluous response.WriteHeader call

In my code, I’ve a loop that processing set of files (based on what available at pre-specified folder), and based on the output of each processed file, some info is sent to the client, so I wrote the below:

	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) // -< Error from here
		w.Write(data)
	}

Above working correctly if the folder has a single file, but if has more than one, I got the error: http: superfluous response.WriteHeader call
I understood the error is due to using w.WriteHeader(http.StatusOK) which can not be used more than once to be set, but I need it to be set for the client to process the returned data.
If I remove http.StatusOK , then I get the returned as plain text not as JSON!

How can I fix this code, so that I can return data directly to the client upon processing each file.

The WriteHeader method and SetHeader method must be before the Write method, and the Write method for the first for loop is before the WriteHeader method for the second for loop.

1 Like

Calling w.Write requires http status code to be set before otherwise it sets default StatusOK. Setting status code multiple times throws superfluous log.
. You may better look for chunked transfer-encoding with 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

No need to set the status as http.StatusOK, as this is the default.
Also no need to explicitly set the transfer-encoding as chunked, as this is also the default.

I would just create a json encoder which sends its output to the http response,
and call it with each output object

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

[/quote]