How to handle template execution errors in Go
Using the template.Execute()
function may produce an error. This snippet describes how to prevent that error from causing a broken page to be rendered to users.
A common problem when dealing with templates in Go is the handling of errors when attempting to render a template. An error may occur in the process of rendering a template when an attempt is made to access data that does not exist, or when a function that was invoked returns an error.
package main
import (
"html/template"
"net/http"
)
var tpl = template.Must(template.ParseFiles("index.html"))
func handler(w http.ResponseWriter, r *http.Request) {
err := tpl.Execute(w, nil)
if err != nil {
// How to handle error now that parts of the template
// have already been written and HTTP status code cannot
// be changed
}
}
Once the template starts executing to the ResponseWriter
, a 200 status code is
implied and it’s not possible to undo what has already been sent even if an
error occurs. The effect is that when an error occurs in the template, the
execution is halted but the parts preceding the point where the error occurred
have already been sent, so users may end up with a half rendered page which
neither works nor conveys the necessary error message.
To avoid this problem, it’s better to write the entire template to a temporary
buffer first then copy it to the ResponseWriter
once we know that it has
successfully executed. Here’s how it can be achieved using the bytes
package:
package main
import (
"bytes"
"html/template"
"net/http"
)
var tpl = template.Must(template.ParseFiles("index.html"))
func handler(w http.ResponseWriter, r *http.Request) {
// Create an empty buffer that is ready to use
var buf bytes.Buffer
// Write the template to the buffer first
err := tpl.Execute(&buf, nil)
if err != nil {
// Handle the error if any and return
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Template executed successfully to the buffer.
// Now, copy it over to the ResponseWriter
// This implies a 200 OK status code
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
buf.WriteTo(w)
}
This way, we can render the appropriate error page if something happens when executing the template. This practice also makes it easier to test our HTTP handlers because we now know for sure that a 200 OK response means that the template rendered successfully.
If you don’t want to create a new empty buffer for every request, you can use a buffer pool to help reduce allocations and garbage collection.
Thanks for reading, and happy coding!