Skip to content

Instantly share code, notes, and snippets.

@barthr
Last active July 22, 2018 16:42
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barthr/e0605f538710a73ca390fb4803eed142 to your computer and use it in GitHub Desktop.
Save barthr/e0605f538710a73ca390fb4803eed142 to your computer and use it in GitHub Desktop.
Wrapper for clean handlers in Go
package web
import (
"bytes"
"encoding/json"
"io"
"net/http"
)
type errorResponse struct {
Error string `json:"error"`
}
type Action func(r *http.Request) *Response
type Headers map[string]string
type Response struct {
Status int
ContentType string
Content io.Reader
Headers Headers
}
func Error(status int, err error, headers Headers) *Response {
return &Response{
Status: status,
Content: bytes.NewBufferString(err.Error()),
Headers: headers,
}
}
func ErrorJSON(status int, err error, headers Headers) *Response {
errResp := errorResponse{
Error: err.Error(),
}
b, err := json.Marshal(errResp)
if err != nil {
return Error(http.StatusInternalServerError, err, headers)
}
return &Response{
Status: status,
ContentType: "application/json",
Content: bytes.NewBuffer(b),
Headers: headers,
}
}
func Data(status int, content []byte, headers Headers) *Response {
return &Response{
Status: status,
Content: bytes.NewBuffer(content),
Headers: headers,
}
}
func DataJSON(status int, v interface{}, headers Headers) *Response {
b, err := json.Marshal(v)
if err != nil {
return ErrorJSON(http.StatusInternalServerError, err, headers)
}
return &Response{
Status: status,
ContentType: "application/json",
Content: bytes.NewBuffer(b),
Headers: headers,
}
}
func DataWithReader(status int, r io.Reader, headers Headers) *Response {
return &Response{
Status: status,
Content: r,
Headers: headers,
}
}
func (a Action) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if response := a(r); response != nil {
if response.ContentType != "" {
rw.Header().Set("Content-Type", response.ContentType)
}
for k, v := range response.Headers {
rw.Header().Set(k, v)
}
rw.WriteHeader(response.Status)
_, err := io.Copy(rw, response.Content)
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
}
} else {
rw.WriteHeader(http.StatusOK)
}
}
@nowylie
Copy link

nowylie commented Jul 11, 2017

At first I really liked your approach, but after some thought I'm not sure that you're really gaining much value. Why not just write some utility functions? gist

@lmas
Copy link

lmas commented Jul 11, 2017

@barthr
Copy link
Author

barthr commented Aug 7, 2017

@nowylie Because this way you can keep the handlers clear and clean, without the use of early return in. I explain it in more detail here: https://blog.bartfokker.nl/

@barthr
Copy link
Author

barthr commented Aug 7, 2017

@imas thank you for sharing this link 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment