Say I have a html/template
like the following:
<html>
<body>
<p>{{SomeFunc .SomeData}}</p>
</body>
and sometimes SomeFunc
returns an error. Is there an idiomatic way to deal with this?
If I write directly to the ResponseWriter
, then a status code 200 has already been written before I encounter the error.
var tmpl *template.Template
func Handler(w http.ResponseWriter, r *http.Request) {
err := tmpl.Execute(w, data)
// "<html><body><p>" has already been written...
// what to do with err?
}
Preferably I would return a status code 400 or some such, but I can't see a way to do this if I use template.Execute
directly on the ResponseWriter
. Is there something I'm missing?
The idiomatic way of handling errors in Go is to compare the returned error to nil . A nil value indicates that no error has occurred and a non-nil value indicates the presence of an error. In our case, we check whether the error is not nil in line no. 10.
Errors can be returned as nil , and in fact, it's the default, or “zero”, value of on error in Go. This is important since checking if err != nil is the idiomatic way to determine if an error was encountered (replacing the try / catch statements you may be familiar with in other programming languages).
You can create wrapped errors by using the %w flag with the fmt. Errorf function as shown in the following example. As you can see the application prints both the new error created using fmt. Errorf as well as the old error message that was passed to the %w flag.
This method is also known as try-catch-finally In this method, in the try block the error-free code has been executed and if it has found some issue it will be handled in the catch block and finally block will be executed all the things at any cost.
Since the template engine generates the output on-the-fly, parts of the template preceding the SomeFunc
call are already sent to the output. And if the output is not buffered, they (along with the HTTP 200 status) may already be sent.
You can't do anything about that.
What you can do is perform the check before you call template.Execute()
. In trivial case it should be enough to call SomeFunc()
and check its return value. If you choose this path and the return value of SomeFunc()
is complex, you do not have to call it again from the template, you can simply pass its return value to the params you pass to the template and refer to this value in the template (so SomeFunc()
won't have to be executed twice).
If this is not enough or you can't control it, you can create a bytes.Buffer
, execute your template directed into this buffer, and after the Execute()
returns, check if there were errors. If there were errors, send back a proper error message / page. If everything went ok, you can just send the content of the buffer to the ResponseWriter
.
This could look something like this:
buf := &bytes.Buffer{}
err := tmpl.Execute(buf, data)
if err != nil {
// Send back error message, for example:
http.Error(w, "Hey, Request was bad!", http.StatusBadRequest) // HTTP 400 status
} else {
// No error, send the content, HTTP 200 response status implied
buf.WriteTo(w)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With