From 20ff960e4ee6cb0e7c8d69b49947475fd5c8ea3f Mon Sep 17 00:00:00 2001 From: Alexander Zimmermann Date: Sat, 29 Jun 2019 10:55:56 +0200 Subject: [PATCH 1/2] Adding support for default error messages --- README.md | 28 ++++++++++++++++++++++++++++ go.mod | 3 +++ response.go | 26 +++++++++++++++++++++++--- response_test.go | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 go.mod diff --git a/README.md b/README.md index d5e23d1..dda5477 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,34 @@ See [here](https://httpstatuses.com/) for a complete list of HTTP responses, alo Please submit a PR if you want to add to this list. Only the most common response types have been included. +## To Long, Don't Write + +Sometimes you don't need to return a specific content-message but don't want the response body to be empty. +In this case you can use the `DefaultMessage()` for responding with json containing the default message for the corresponding status code. + +```go +package main + +import ( + "net/http" + resp "github.com/nicklaw5/go-respond" +) + +func main() { + http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) { + // ... + if !authenticated { + resp.NewResponse(w).DefaultMessage(). + Unauthorized(nil) + } + // ... + }) + http.ListenAndServe(":8080", nil) +} +``` + +Would respond with `{"status":401,"message":"Unauthorized"}` + ## Handling Errors The best option for handling errors that may occur while marshalling the JSON response, is to use [Negroni's Recovery middleware](https://github.com/urfave/negroni#recovery). Here's an example: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dfd9053 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/nicklaw5/go-respond + +go 1.12 diff --git a/response.go b/response.go index edbd161..78631ee 100644 --- a/response.go +++ b/response.go @@ -7,8 +7,15 @@ import ( // Response is the HTTP response type Response struct { - Writer http.ResponseWriter - Headers map[string]string + Writer http.ResponseWriter + Headers map[string]string + DefMessage bool +} + +// DefaultMessageResponse is for transporting a default http message +type DefaultMessageResponse struct { + Status int `json:"status"` + Message string `json:"message"` } // NewResponse creates and returns a new response @@ -21,6 +28,11 @@ func NewResponse(w http.ResponseWriter) *Response { } } +func (resp *Response) DefaultMessage() *Response { + resp.DefMessage = true + return resp +} + // DeleteHeader deletes a single header from the response func (resp *Response) DeleteHeader(key string) *Response { resp.Writer.Header().Del(key) @@ -41,16 +53,24 @@ func (resp *Response) writeResponse(code int, v interface{}) error { resp.writeStatusCode(code) + if v == nil && resp.DefMessage { + v = DefaultMessageResponse{ + Status: code, + Message: http.StatusText(code), + } + } + if v != nil { body, err := json.Marshal(v) if err != nil { panic(err) } - + // can just return an error when connection is hijacked or content-size is longer then declared. if _, err := resp.Writer.Write(body); err != nil { panic(err) } } + return nil } diff --git a/response_test.go b/response_test.go index 4469604..d0ae4eb 100644 --- a/response_test.go +++ b/response_test.go @@ -3,11 +3,10 @@ package respond_test import ( "errors" "fmt" + resp "github.com/nicklaw5/go-respond" "net/http" "net/http/httptest" "testing" - - resp "github.com/nicklaw5/go-respond" ) func newRequest(t *testing.T, method string) *http.Request { @@ -42,6 +41,42 @@ func validateResponseHeader(responseHeaderValue string, expectedHeaderValue stri return nil } +func TestDefaultMessage(t *testing.T) { + t.Parallel() + + req := newRequest(t, "GET") + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + resp.NewResponse(w).DefaultMessage(). + Unauthorized(nil) + }) + handler.ServeHTTP(rr, req) + + if err := validateResponseBody(rr.Body.String(), "{\"status\":401,\"message\":\"Unauthorized\"}"); err != nil { + t.Fatal(err) + } +} + +func TestRespondInvalidType(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("responding with invalid type (channel) should have caused a panic") + } + }() + + t.Parallel() + + req := newRequest(t, "GET") + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + resp.NewResponse(w).DefaultMessage(). + Unauthorized(make(chan int)) + }) + handler.ServeHTTP(rr, req) +} + func TestContentTypeHeader(t *testing.T) { t.Parallel() From 1fb95430a36188b1b012319756e74318f3df620c Mon Sep 17 00:00:00 2001 From: Alexander Zimmermann Date: Sun, 30 Jun 2019 08:11:07 +0200 Subject: [PATCH 2/2] removed go.mod and fixed some more imports --- go.mod | 3 --- response_test.go | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 go.mod diff --git a/go.mod b/go.mod deleted file mode 100644 index dfd9053..0000000 --- a/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/nicklaw5/go-respond - -go 1.12 diff --git a/response_test.go b/response_test.go index 98a9e02..4227166 100644 --- a/response_test.go +++ b/response_test.go @@ -3,7 +3,6 @@ package respond import ( "errors" "fmt" - resp "github.com/nicklaw5/go-respond" "net/http" "net/http/httptest" "testing" @@ -48,7 +47,7 @@ func TestDefaultMessage(t *testing.T) { rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - resp.NewResponse(w).DefaultMessage(). + NewResponse(w).DefaultMessage(). Unauthorized(nil) }) handler.ServeHTTP(rr, req) @@ -71,7 +70,7 @@ func TestRespondInvalidType(t *testing.T) { rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - resp.NewResponse(w).DefaultMessage(). + NewResponse(w).DefaultMessage(). Unauthorized(make(chan int)) }) handler.ServeHTTP(rr, req)