You don't need to write http.ResponseWriter
wrappers anymore.
It has become a common thing to write a wrapper of http.ResponseWriter
because at some point it was a need to get request response information like the response status. In a complete request flow, a lot of middlewares required something (some requires the status, the number of bytes wrote, the route pattern used, ...). Moreover, some middlewares are also interacting with the response (like a panic or a timeout handler that sets the response status) causing unwanted behaviour (like a net/http log saying the response status should only we wrote one time). The naive approach of wrapping the http.ResponseWriter
introduce some flaws and/or does not help to fix the already existing ones.
For example here:
type responseWriterWrapper struct{
writer http.ResponseWriter
status int
}
func (rww *responseWriterWrapper) WriteHeader(status int) {
rww.status = status
writer.WriteHeader(status)
}
// ...
If the original http.ResponseWriter
was implementing http.Flusher
, it is not the case anymore. To fix that we can add the Flush
method:
func (rww *responseWriterWrapper) Flush() {
if f, ok := (rww.writer).(http.Flusher); ok {
f.Flush()
}
}
but now our wrapper always implements the http.Flusher
interface which can also cause unwanted behaviour.
For all these reasons I decided to write my last wrapper of http.ResponseWriter
and it has the following features:
- records the http response status, the number of bytes wrote, the execution time of the next handler, and helps to retrieve the route matching pattern.
- writes the response status at the last possible time, to prevent the multiple status wrote error
- keeps the same net/http interfaces implementation of the wrapped
http.ResponseWriter
- heavily tested
- multi-thread safe
- super simple to use
// during the router setup...
router.Use(
httpinfo.Record(),
// other middlewares goes after, even the panic recover one
myMiddleware,
)
func myMiddleware(next http.Handler) http.Handler {
return func (rw http.ResponseWriter, r *http.Request ) {
// call the next handler
next.ServeHTTP(rw, r)
// within any request handler, you're now able to get response info
var (
status = httpinfo.Status(r)
route = httpinfo.Route(r)
contentLength = httpinfo.ContentLength(r)
latency = httpinfo.ExecutionTime(r)
)
// ...
}
}
More doc and examples in the httpinfo's godoc
This project is under the MIT licence, please see the LICENCE file.