4

Using Golang's net/http package, how can I check if the ResponseWriter has been written to? I am receiving the following error message:

http: multiple response.WriteHeader calls

Of course I can return booleans and such from my functions indicating weather I wrote to the ResponseWriter with a redirect or something, which I tried, but surely I am able to check if the ResponseWriter has been written to before I write to it with an easy method.

I am looking for a function that would look something like the following which I can use before I write to the ResponseWriter:

if w.Header().Get("Status-Code") == "" {
    http.Redirect(w, r, "/", http.StatusSeeOther)
} else {
    fmt.Println("Response Writer has already been written to.")
}

The code above doesn't seem to work... anyone have any idea how to check if the ResponseWriter has been written to or not?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Ari Seyhun
  • 11,506
  • 16
  • 62
  • 109
  • I found a possible solution. Every time I write to the ResponseWriter with a redirect or file server, I could also set a custom header `w.Header().Set("custom", "1")` indicating it's been set and I could check with `w.Header().Get("custom") == "1"` – Ari Seyhun Sep 09 '16 at 16:28
  • 1
    You could make your own version of `http.ResponseWriter` that sets a bool to true when `WriteHeader` is called. See https://play.golang.org/p/o6Tp9urYZZ. – Andy Schweig Sep 09 '16 at 19:25
  • Have a look at the [Gin framework](https://github.com/gin-gonic/gin/blob/4a6bc4aac4607e253bcda67c8c5bcda693d2388e/response_writer.go). It implements a `Written()` method on the ResponseWriter – fl0cke Sep 09 '16 at 22:13
  • See example implementation of [storing and accessing written status code](http://stackoverflow.com/a/34018537/1705598). – icza Sep 09 '16 at 23:05
  • Possible duplicate of [How can I tell if net/http's ResponseWriter.Write() has been called?](http://stackoverflow.com/questions/31897411/how-can-i-tell-if-net-https-responsewriter-write-has-been-called) – Jonathan Hall Mar 13 '17 at 10:01

1 Answers1

6

The only way to do this is with a custom implementation of http.ResponseWriter:

type doneWriter struct {
    http.ResponseWriter
    done bool
}

func (w *doneWriter) WriteHeader(status int) {
    w.done = true
    w.ResponseWriter.WriteHeader(status)
}

func (w *doneWriter) Write(b []byte) (int, error) {
    w.done = true
    return w.ResponseWriter.Write(b)
}

func myMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        dw := &doneWriter{ResponseWriter: w}
        next.ServeHTTP(dw, r)
        if dw.done {
            // Something already wrote a response
            return
        }
        // Nothing else wrote a response
        w.WriteHeader(http.StatusOK)
        // Whatever you want here
    }
}

I also threw together a simple package to handle this for me. Feel free to use it as well if you like.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189