package sleepy import ( "encoding/json" "errors" "fmt" "net/http" "net/url" ) const ( GET = "GET" POST = "POST" PUT = "PUT" DELETE = "DELETE" ) // GetSupported is the interface that provides the Get // method a resource must support to receive HTTP GETs. type GetSupported interface { Get(url.Values) (int, interface{}) } // PostSupported is the interface that provides the Post // method a resource must support to receive HTTP POST. type PostSupported interface { Post(url.Values) (int, interface{}) } // PutSupported is the interface that provides the Put // method a resource must support to receive HTTP PUT. type PutSupported interface { Put(url.Values) (int, interface{}) } // DeleteSupported is the interface that provides the Delete // method a resource must support to receive HTTP DELETE. type DeleteSupported interface { Delete(url.Values) (int, interface{}) } // An API manages a group of resources by routing to requests // to the correct method on a matching resource and marshalling // the returned data to JSON for the HTTP response. // // You can instantiate multiple APIs on separate ports. Each API // will manage its own set of resources. type API struct { mux *http.ServeMux } // NewAPI allocates and returns a new API. func NewAPI() *API { return &API{} } func (api *API) requestHandler(resource interface{}) http.HandlerFunc { return func(rw http.ResponseWriter, request *http.Request) { if request.ParseForm() != nil { rw.WriteHeader(http.StatusBadRequest) return } var handler func(url.Values) (int, interface{}) switch request.Method { case GET: if resource, ok := resource.(GetSupported); ok { handler = resource.Get } case POST: if resource, ok := resource.(PostSupported); ok { handler = resource.Post } case PUT: if resource, ok := resource.(PutSupported); ok { handler = resource.Put } case DELETE: if resource, ok := resource.(DeleteSupported); ok { handler = resource.Delete } } if handler == nil { rw.WriteHeader(http.StatusMethodNotAllowed) return } code, data := handler(request.Form) content, err := json.Marshal(data) if err != nil { rw.WriteHeader(http.StatusInternalServerError) } rw.WriteHeader(code) rw.Write(content) } } // AddResource adds a new resource to an API. The API will route // requests that match one of the given paths to the matching HTTP // method on the resource. func (api *API) AddResource(resource interface{}, paths ...string) { if api.mux == nil { api.mux = http.NewServeMux() } for _, path := range paths { api.mux.HandleFunc(path, api.requestHandler(resource)) } } // Start causes the API to begin serving requests on the given port. func (api *API) Start(port int) error { if api.mux == nil { return errors.New("You must add at least one resource to this API.") } portString := fmt.Sprintf(":%d", port) return http.ListenAndServe(portString, api.mux) }