DEV Community

Cover image for Side Effects of Go's response.WriteHeader
Kenta Takeuchi
Kenta Takeuchi

Posted on • Originally published at bmf-tech.com

Side Effects of Go's response.WriteHeader

This article was originally published on bmf-tech.com.

Overview

This is a note about the side effects of Go's response.WriteHeader.

superfluous response.WriteHeader

The following is a straightforward example, but if you call WriteHeader multiple times as shown below, you will get an error: http: superfluous response.WriteHeader call from main.handler.

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.WriteHeader(http.StatusInternalServerError) // Error!
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8888", nil)
}
Enter fullscreen mode Exit fullscreen mode

When called multiple times like this, the status code from the first call is adopted, and the status code from the last call is ignored. (Is it related to HTTP specifications?)

You can get a sense of the specifications by looking at these.

Solution

In the straightforward example above, you can simply adjust the implementation to call WriteHeader only once. However, let's assume there are cases where WriteHeader is set multiple times due to implementation reasons.

import (
    "bytes"
    "html/template"
    "net/http"
)

// Function called for rendering error pages
func ExecuteTpl(w http.ResponseWriter) error {
    err := template.Must(template.ParseFiles("index.html")).Execute(w, nil)
    w.WriteHeader(http.StatusInternalServerError)
    if err != nil {
        return err
    }

    return nil
}
Enter fullscreen mode Exit fullscreen mode

Suppose WriteHeader has already been called before this function, causing an error.

To avoid errors in such situations, you can write to a buffer first and then call WriteHeader at the end.

package main

import (
    "bytes"
    "html/template"
    "net/http"
)

func ExecuteTpl(w http.ResponseWriter) error {
    var buf bytes.Buffer

    w.WriteHeader(http.StatusInternalServerError)
    err := template.Must(template.ParseFiles("index.html")).Execute(w, nil)
    if err != nil {
        return err
    }

    buf.WriteTo(w)

    return nil
}
Enter fullscreen mode Exit fullscreen mode

Others

When there is an error during the call to template's Execute, execution stops, but it seems that writing to the response may partially start, so it seems better to call WriteHeader before Execute. (Probably)

References

Top comments (0)