Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for default http-messages #5

Merged
merged 3 commits into from
Jun 30, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/nicklaw5/go-respond

go 1.12
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any real need to include this file if we don't have any dependencies?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, otherwise i could not use it outside my gopath. The package names did not resolve right in the tests.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe the absence of a go.mod file would break your tests. Can you provide an example of the issue you're running into?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Nick, i did not say it breaks the build generally. It Just cant find the right package if you are outside of the gopath.

Why is this such a big thing? Go Modules are a Feature added in Go 1.12. They do not give any disadvantages and you could just ignore them if working not with go 1.12.

Copy link
Owner

@nicklaw5 nicklaw5 Jun 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may have to pull in your dependencies before running tests. For example

go get ./...
go test ./...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When i delete the go.mod file and try to test this project outside from $GOPATH or $GOROOT, it will give me following error output:

PS C:\Users\Alex\Desktop\go-respond> go test ./
# _/C_/Users/Alex/Desktop/go-respond
error_test.go:8:2: cannot find package "github.com/nicklaw5/go-respond" in any of:
        C:\Go\src\github.com\nicklaw5\go-respond (from $GOROOT)
        D:\Go\src\github.com\nicklaw5\go-respond (from $GOPATH)
FAIL    _/C_/Users/Alex/Desktop/go-respond [setup failed]
PS C:\Users\Alex\Desktop\go-respond>

Adding this file would remove the need of placing this project inside your $GOPATH or $GOROOT, even if you don't have any dependencies. You declare a package-name which would otherwise not be found.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean. That can be fixed. Let me fix it and then we rebase your branch onto master.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok that's been fixed on the master branch. You'll need to rebase this branch:

git rebase master

Then apply the changes in 4ab9d9b.

You should then be able to run your tests without any issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged your changes, removed the go.mod file and fixed some more importing issues.

26 changes: 23 additions & 3 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
}

Expand Down
39 changes: 37 additions & 2 deletions response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()

Expand Down