Say I have this http handler:
func SomeHandler(w http.ResponseWriter, r *http.Request) {
data := GetSomeData()
_, err := w.Write(data)
}
Should I check the error returned by w.Write
? Examples I've seen just ignore it and do nothing. Also, functions like http.Error()
do not return an error to be handled.
It's up to you. My advice is that unless the documentation of some method / function explicitly states that it never returns a non-nil
error (such as bytes.Buffer.Write()
), always check the error and the least you can do is log it, so if an error occurs, it will leave some mark which you can investigate should it become a problem later.
This is also true for writing to http.ResponseWriter
.
You might think ResponseWriter.Write()
may only return errors if sending the data fails (e.g. connection closed), but that is not true. The concrete type that implements http.ResponseWriter
is the unexported http.response
type, and if you check the unexported response.write()
method, you'll see it might return a non-nil
error for a bunch of other reasons.
Reasons why ResponseWriter.Write()
may return a non-nil
error:
http.Hijacker
): http.ErrHijacked
http.ErrContentLength
http.ErrBodyNotAllowed
Even if you can't do anything with the error, logging it may be of great help debugging the error later on. E.g. you (or someone else in the handler chain) hijacked the connection, and you attempt to write to it later; you get an error (http.ErrHijacked
), logging it will reveal the cause immediately.
If you can't do anything with the occasional error and it's not a "showstopper", you may create and use a simple function that does the check and logging, something like this:
func logerr(n int, err error) {
if err != nil {
log.Printf("Write failed: %v", err)
}
}
Using it:
logerr(w.Write(data))
If you don't even want to use the logerr()
function all the time, you may create a wrapper for http.ResponseWriter
which does this "automatically":
type LogWriter struct {
http.ResponseWriter
}
func (w LogWriter) Write(p []byte) (n int, err error) {
n, err = w.ResponseWriter.Write(p)
if err != nil {
log.Printf("Write failed: %v", err)
}
return
}
Using it:
func SomeHandler(w http.ResponseWriter, r *http.Request) {
w = LogWriter{w}
w.Write([]byte("hi"))
}
Using LogWriter
as a wrapper around http.ResponseWriter
, should writes to the original http.ResponseWriter
fail, it will be logged automatically.
This also has the great benefit of not expecting a logger function to be called, so you can pass a value of your LogWriter
"down" the chain, and everyone who attempts to write to it will be monitored and logged, they don't have to worry or even know about this.
But care must be taken when passing LogWriter
down the chain, as there's also a downside to this: a value of LogWriter
will not implement other interfaces the original http.ResponseWriter
might also do, e.g. http.Hijacker
or http.Pusher
.
Here's an example on the Go Playground that shows this in action, and also shows that LogWriter
will not implement other interfaces; and also shows a way (using 2 "nested" type assertions) how to still get out what we want from LogWriter
(an http.Pusher
in the example).
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