From 40bcabf1ae0229e6b721d69823d34fd2b080c129 Mon Sep 17 00:00:00 2001 From: Ashish Hanwadikar Date: Sun, 26 Nov 2017 09:38:57 -0800 Subject: [PATCH] Using embedding instead of type check for http methods Inherit not supported method implementation for HTTP methods. --- core.go | 73 ++++++++++++++++++++++------------------------------ core_test.go | 4 ++- 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/core.go b/core.go index 631d76d..fbc0d6d 100644 --- a/core.go +++ b/core.go @@ -17,42 +17,42 @@ const ( PATCH = "PATCH" ) -// GetSupported is the interface that provides the Get -// method a resource must support to receive HTTP GETs. -type GetSupported interface { +type Resource interface { Get(url.Values, http.Header) (int, interface{}, http.Header) + Post(url.Values, http.Header) (int, interface{}, http.Header) + Put(url.Values, http.Header) (int, interface{}, http.Header) + Delete(url.Values, http.Header) (int, interface{}, http.Header) + Head(url.Values, http.Header) (int, interface{}, http.Header) + Patch(url.Values, http.Header) (int, interface{}, http.Header) } -// PostSupported is the interface that provides the Post -// method a resource must support to receive HTTP POSTs. -type PostSupported interface { - Post(url.Values, http.Header) (int, interface{}, http.Header) +type NotSupported struct {} + +func (NotSupported) Get(url.Values, http.Header) (int, interface{}, http.Header) { + return http.StatusMethodNotAllowed, nil, nil } -// PutSupported is the interface that provides the Put -// method a resource must support to receive HTTP PUTs. -type PutSupported interface { - Put(url.Values, http.Header) (int, interface{}, http.Header) +func (NotSupported) Post(url.Values, http.Header) (int, interface{}, http.Header) { + return http.StatusMethodNotAllowed, nil, nil } -// DeleteSupported is the interface that provides the Delete -// method a resource must support to receive HTTP DELETEs. -type DeleteSupported interface { - Delete(url.Values, http.Header) (int, interface{}, http.Header) +func (NotSupported) Put(url.Values, http.Header) (int, interface{}, http.Header) { + return http.StatusMethodNotAllowed, nil, nil } -// HeadSupported is the interface that provides the Head -// method a resource must support to receive HTTP HEADs. -type HeadSupported interface { - Head(url.Values, http.Header) (int, interface{}, http.Header) +func (NotSupported) Delete(url.Values, http.Header) (int, interface{}, http.Header) { + return http.StatusMethodNotAllowed, nil, nil } -// PatchSupported is the interface that provides the Patch -// method a resource must support to receive HTTP PATCHs. -type PatchSupported interface { - Patch(url.Values, http.Header) (int, interface{}, http.Header) +func (NotSupported) Patch(url.Values, http.Header) (int, interface{}, http.Header) { + return http.StatusMethodNotAllowed, nil, nil +} + +func (NotSupported) Head(url.Values, http.Header) (int, interface{}, http.Header) { + return http.StatusMethodNotAllowed, nil, nil } + // An API manages a group of resources by routing requests // to the correct method on a matching resource and marshalling // the returned data to JSON for the HTTP response. @@ -69,7 +69,7 @@ func NewAPI() *API { return &API{} } -func (api *API) requestHandler(resource interface{}) http.HandlerFunc { +func (api *API) requestHandler(resource Resource) http.HandlerFunc { return func(rw http.ResponseWriter, request *http.Request) { if request.ParseForm() != nil { @@ -81,43 +81,32 @@ func (api *API) requestHandler(resource interface{}) http.HandlerFunc { 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 - } case HEAD: - if resource, ok := resource.(HeadSupported); ok { handler = resource.Head - } case PATCH: - if resource, ok := resource.(PatchSupported); ok { handler = resource.Patch - } } - if handler == nil { - rw.WriteHeader(http.StatusMethodNotAllowed) + code, data, header := handler(request.Form, request.Header) + + if code < http.StatusOK || code > 300 { + rw.WriteHeader(code) return } - code, data, header := handler(request.Form, request.Header) - content, err := json.MarshalIndent(data, "", " ") if err != nil { rw.WriteHeader(http.StatusInternalServerError) return } + for name, values := range header { for _, value := range values { rw.Header().Add(name, value) @@ -143,7 +132,7 @@ func (api *API) Mux() *http.ServeMux { // 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) { +func (api *API) AddResource(resource Resource, paths ...string) { for _, path := range paths { api.Mux().HandleFunc(path, api.requestHandler(resource)) } @@ -152,7 +141,7 @@ func (api *API) AddResource(resource interface{}, paths ...string) { // AddResourceWithWrapper behaves exactly like AddResource but wraps // the generated handler function with a give wrapper function to allow // to hook in Gzip support and similar. -func (api *API) AddResourceWithWrapper(resource interface{}, wrapper func(handler http.HandlerFunc) http.HandlerFunc, paths ...string) { +func (api *API) AddResourceWithWrapper(resource Resource, wrapper func(handler http.HandlerFunc) http.HandlerFunc, paths ...string) { for _, path := range paths { api.Mux().HandleFunc(path, wrapper(api.requestHandler(resource))) } diff --git a/core_test.go b/core_test.go index 8027bfe..51dc194 100644 --- a/core_test.go +++ b/core_test.go @@ -7,7 +7,9 @@ import ( "testing" ) -type Item struct{} +type Item struct{ + NotSupported +} func (item Item) Get(values url.Values, headers http.Header) (int, interface{}, http.Header) { items := []string{"item1", "item2"}