Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic way to handle template errors in golang

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?

like image 620
chowey Avatar asked Jun 13 '15 17:06

chowey


People also ask

How do you handle errors in Golang?

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.

Which of the following is considered good practices when handling errors in Golang?

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

How do you wrap an error in Go?

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.

How do you use try catch in Golang?

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.


1 Answers

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)
}
like image 186
icza Avatar answered Oct 11 '22 00:10

icza