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/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 5851980..4227166 100644 --- a/response_test.go +++ b/response_test.go @@ -40,6 +40,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) { + 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) { + NewResponse(w).DefaultMessage(). + Unauthorized(make(chan int)) + }) + handler.ServeHTTP(rr, req) +} + func TestContentTypeHeader(t *testing.T) { t.Parallel()