Skip to content

Instantly share code, notes, and snippets.

@ericchiang
Last active February 24, 2016 21:17
Show Gist options
  • Save ericchiang/4928eb02991f9e4afaa5 to your computer and use it in GitHub Desktop.
Save ericchiang/4928eb02991f9e4afaa5 to your computer and use it in GitHub Desktop.
/*
Package api provides facilities for API specific handlers.
var errDecode = api.NewError("invalid_request", "Failed to decode request.", http.StatusBadRequest)
func init() {
// Register errors for use with WriteError.
api.Register(user.ErrUserExists, api.NewError("invalid_request", "Username already exists.", http.StatusBadRequest))
}
// Define a handler that uses the api.ResponseWriter as a first argument.
func (u *userAPI) handleCreateUser(w api.ResponseWriter, r *http.Request) {
var req createUserReq
if err := json.Unmarshal.(r.Body); err != nil {
w.WriteError(errDecode) // Write an api.Error type.
return
}
usr, err := u.CreateUser(req)
if err != nil {
// If this error is registered, it will write the associated error.
// If not, it will log the error and return an internal server error.
w.WriteError(err)
return
}
w.Write(usr)
}
// Define middleware which uses the API response writer.
func (u *userAPI) auth(h api.Handler) api.Handler {
return func(w api.ResponseWriter, r *http.Request) {
if err := u.isAuthed(r); err != nil {
w.WriteError(err) // or a more specific error
return
}
h(w, r)
}
}
// api.Handler types can be used as a http.Handler.
func (u *userAPI) register(mux *http.ServerMux) {
mux.Handle(u.auth(u.handleCreateUser))
}
*/
package api
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"sync"
"github.com/coreos/dex/pkg/log"
"github.com/coreos/dex/user/api"
)
var errorMap = make(map[error]Error)
func NewError(errCode, description, statusCode int) api.Error {
return Error{errCode, description, statusCode}
}
type Error struct {
Err string `json:"error"`
Description string `json:"error_description"`
// The HTTP StatusCode
Code int `json:"-"`
}
func (err Error) Error() string {
if err.Description == "" {
return err.Err
}
return err.Err + ": " + err.Description
}
// RegisterError associates an generic error with an api.Error.
//
// If RegisterError is called twice with the same error it panics.
func RegisterError(err error, apiErr api.Error) {
if _, ok := errorMap[err]; ok {
panic(fmt.Sprintf("error %v registered twice", err))
}
errorMap[err] = apiErr
}
type ResponseWriter interface {
// Write is a convenience method for calling WriteResponse with a 200 status code.
Write(v interface{})
// Write JSON encodes an error an writes it to the response body.
//
// If err is of type api.Error, it is written. If the error has been registered
// with api.RegisterError, the associated api.Error is written. If neither of
// these conditions hold, the error is logged and an internal server error is
// written.
WriteError(err error)
// WriteResponse JSON encodes the provided object and writes it to the response
// with the provided status code.
WriteResponse(v interface{}, status int)
}
/*
Handler satisfies the http.Handler interface.
*/
type Handler func(ResponseWriter, *http.Request)
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h(&responseWriter{w}, r)
}
// responseWriter is the internal implementation of the ResponseWriter
// interface.
type responseWriter struct {
w http.ResponseWriter
}
func (w *responseWriter) Write(v interface{}) {
w.WriteResponse(v, http.StatusOK)
}
func (w *responseWriter) WriteError(err error) {
var e Error
var ok bool
if e, ok = err.(Error); !ok {
if e, ok = errorMap[err]; !ok {
log.Errorf("Unrecognized error written: %v", err)
e = Error{"internal_failure", "", http.StatusInternalServerError}
}
}
w.WriteResponse(e, e.Code)
}
func (w *responseWriter) WriteResponse(v interface{}, status int) {
data, err := json.Marshal(v)
if err != nil {
log.Errorf("Failed to marshal response: %v", err)
w.w.WriteHeader(http.StatusInternalServerError)
return
}
w.w.Header().Set("Content-Type", "application/json")
w.w.Header().Set("Content-Length", strconv.Itoa(len(data)))
w.w.WriteHeader(status)
w.w.Write(data)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment