Last active
January 26, 2019 16:46
-
-
Save lunemec/772e8191335e33745ce76d1f1048e476 to your computer and use it in GitHub Desktop.
upgraded handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Handler is http handler func that returns error which will be written | |
// to ResponseWriter automatically. | |
type Handler func(http.ResponseWriter, *http.Request) error | |
// Response represents an error with an associated HTTP status code. | |
type Response struct { | |
Code int | |
RequestID RequestID | |
Err error | |
} | |
// WithUser is any handler that can accept user information. | |
type WithUser func(*auth.User, http.ResponseWriter, *http.Request) error | |
// Allows Response to satisfy the error interface. | |
func (r Response) Error() string { | |
// To avoid panic when printing Response without error. | |
if r.Err == nil { | |
return "" | |
} | |
return r.Err.Error() | |
} | |
// Status returns our HTTP status code. | |
func (r Response) Status() int { | |
if r.Code == 0 { | |
r.Code = 500 | |
} | |
return r.Code | |
} | |
// MarshalJSON to correctly marshal error message. | |
func (r Response) MarshalJSON() ([]byte, error) { | |
type jsonError struct { | |
StatusCode int `json:"status_code,omitempty"` | |
Error string `json:"error,omitempty"` | |
RequestID string `json:"request_id,omitempty"` | |
} | |
return json.Marshal(&jsonError{ | |
StatusCode: r.Status(), | |
Error: r.Error(), | |
RequestID: string(r.RequestID), | |
}) | |
} | |
// Wrap allows us to have http.Handler that can return error which is handled | |
// here and encoded as JSON error with correct http status code. | |
func Wrap(handler Handler) http.Handler { | |
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
err := handler(w, r) | |
if err != nil { | |
switch e := errors.Cause(err).(type) { | |
case Response: | |
w.WriteHeader(e.Status()) | |
// This is not error, but just returning status code. | |
if e.Err == nil { | |
return | |
} | |
default: | |
// Any error types we don't specifically look out for default | |
// to serving a HTTP 500 | |
w.WriteHeader(500) | |
// Wrap normal error to Response struct so that we may JSON encode it. | |
err = Response{Err: err} | |
} | |
encErr := json.NewEncoder(w).Encode(err) | |
if encErr != nil { | |
// If this error happens, it means we are trying to encode something incorrect | |
// and is programming error. | |
panic(fmt.Sprintf("unable to encode error JSON: %s from %s", encErr, err)) | |
} | |
w.Header().Add("Content-Type", "application/json") | |
return | |
} | |
}) | |
} | |
// LoginRequiredHandler is a middleware that enforces users to have correct JWT Authorization token | |
// set and returns HTTP 401 if not. | |
func LoginRequiredHandler(next WithUser) Handler { | |
return func(w http.ResponseWriter, r *http.Request) error { | |
user := auth.GetUser(r) | |
if user == nil { | |
return Response{ | |
Code: http.StatusUnauthorized, | |
Err: errors.New("must have correct Authorization header to access this resource"), | |
} | |
} | |
return next(user, w, r) | |
} | |
} | |
// router is incomplete, just a example | |
func router() { | |
middleware := func(h WithUser) http.Handler { | |
return Wrap( | |
LoginRequiredHandler(h)) | |
} | |
router.Methods(http.MethodPost).Path("/URL/").Handler(middleware(myhandler)) | |
} | |
// I think this is much better as you can return error which is always returned as JSON (via Wrap middleware) | |
// and this handler will ALWAYS be called with the user pre-set. | |
func myhandler(user *auth.User, w http.ResponseWriter, r *http.Request) error { | |
return nil | |
} | |
// Here we have middleware that generates uuid and passes it as requestID, and we parse ?limit=xxx&offset=xxx from url params. | |
func myhandler2(requestID handler.RequestID, limit handler.Limit, offset handler.Offset, user *auth.User, w http.ResponseWriter, r *http.Request) error { | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
/cc @Jonnybcz