Skip to content

Instantly share code, notes, and snippets.

@nubunto
Last active July 13, 2016 13:03
Show Gist options
  • Save nubunto/dc199cd28a0a9a992ff3141c7d94c099 to your computer and use it in GitHub Desktop.
Save nubunto/dc199cd28a0a9a992ff3141c7d94c099 to your computer and use it in GitHub Desktop.
Refactored HTTP error handling/response.
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"time"
"github.com/justinas/alice"
)
type UserResponse struct {
User
}
type Meta struct {
Ok bool `json:"ok"`
Message string `json:"message"`
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
type Response struct {
// HTTP Response Header
Code int `json:"-"`
// Metadata regarding this response
Meta `json:"meta"`
// all the possible responses embedded by the Response struct
*UserResponse `json:"user,omitempty"` // optional structs must be a pointer + omitempty, so it disappears from json output
// Transaction Reference
TxnRef int `json:"txn_ref"`
// If there was an error with this response, set it here
Error error `json:"-"`
}
func (r *Response) prep() {
// sensible defaults go here.
// too much code to stuff inside Render.
r.Meta = Meta{
Ok: true,
Message: "ok",
}
if r.Error != nil {
log.Print(r.Error)
r.Meta = Meta{
Ok: false,
Message: r.Error.Error(),
}
}
}
func (r Response) Render(w http.ResponseWriter) {
r.prep()
b, err := json.Marshal(r)
if err != nil {
w.WriteHeader(500)
log.Printf("Could not marshal JSON for %#v: %v", r, err)
fmt.Fprintf(w, `{"ok": false, message: "Error parsing response JSON."}`)
return
}
w.WriteHeader(r.Code)
w.Write(b)
}
// Since this is all StdLib©, we can take advantage of libraries like Alice
// or implemented handlers in net/http
func timeoutHandler(h http.Handler) http.Handler {
return http.TimeoutHandler(h, 1*time.Second, "timed out")
}
func root(env *Env, w http.ResponseWriter, r *http.Request) Response {
return Response{
Meta: Meta{
Ok: true,
Message: "ok",
},
// force yourself to think about what http code to return.
// good practice?
Code: 200,
TxnRef: 42454245,
}
}
func user(env *Env, w http.ResponseWriter, r *http.Request) Response {
return Response{
UserResponse: &UserResponse{
User: User{
Name: "John Doe",
Age: 94,
},
},
Code: 200,
TxnRef: 123123,
}
}
func errorHandler(env *Env, w http.ResponseWriter, r *http.Request) Response {
log.Println(env)
return Response{
Code: 400,
Error: errors.New("oops. Some random error ocurred because the request was bad"),
}
}
type Env struct {
SomeVar string
OtherVar string
}
type Handler struct {
*Env
H func(e *Env, w http.ResponseWriter, r *http.Request) Response
}
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
response := h.H(h.Env, w, r)
response.Render(w)
}
func main() {
env := &Env{
SomeVar: "Foo",
OtherVar: "Bar",
}
// using std mux + alice, but might as well use Negroni or any other net/http compatible stuff
mux := http.NewServeMux()
mux.Handle("/", Handler{Env: env, H: root})
mux.Handle("/user", Handler{Env: env, H: user})
mux.Handle("/error", Handler{Env: env, H: errorHandler})
chain := alice.New(timeoutHandler).Then(mux)
server := &http.Server{
Addr: ":8080",
Handler: chain,
}
log.Fatal(server.ListenAndServe())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment