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

Allows plain body to be set, rather than just JSON or Form variables #9

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,19 @@ req, err := twitterBase.New().Post(path).BodyForm(tweetParams).Request()

Requests will include an `application/x-www-form-urlencoded` Content-Type header.

#### Plain Request Body

If you prefer, you can just set the `Body` which is then passed through unaltered. Note, no Content-Type header will be set for these, it's up to the developer to correctly set this header using something like `Set("Content-Type", "text/plain")`.


### Extend a Sling

Each distinct Sling generates an `http.Request` (say with some path and query
params) each time `Request()` is called, based on its state. When creating
different kinds of requests using distinct Slings, you may wish to extend
an existing Sling to minimize duplication (e.g. a common client).

Each Sling instance provides a `New()` method which creates an independent copy, so setting properties on the child won't mutate the parent Sling.
Each Sling instance provides a `New()` method which creates an independent copy, so setting properties on the child won't mutate the parent Sling.

```go
const twitterApi = "https://api.twitter.com/1.1/"
Expand Down Expand Up @@ -252,4 +257,3 @@ See the [Contributing Guide](https://gist.github.com/dghubble/be682c123727f70bcf
## License

[MIT License](LICENSE)

18 changes: 18 additions & 0 deletions sling.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
Expand Down Expand Up @@ -33,6 +34,8 @@ type Sling struct {
bodyJSON interface{}
// url tagged body struct (form)
bodyForm interface{}
// simply assigned body
body io.ReadCloser
}

// New returns a new Sling with an http DefaultClient.
Expand Down Expand Up @@ -71,6 +74,7 @@ func (s *Sling) New() *Sling {
queryStructs: append([]interface{}{}, s.queryStructs...),
bodyJSON: s.bodyJSON,
bodyForm: s.bodyForm,
body: s.body,
}
}

Expand Down Expand Up @@ -200,6 +204,18 @@ func (s *Sling) BodyForm(bodyForm interface{}) *Sling {
return s
}

// Body sets the Sling's body. The value pointed to by the
// body will be set raw/as-is as the Body on new requests.
// The body argument should be a string
func (s *Sling) Body(body io.Reader) *Sling {
rc, ok := body.(io.ReadCloser)
if !ok && body != nil {
rc = ioutil.NopCloser(body)
}
s.body = rc
return s
}

// Requests

// Request returns a new http.Request created with the Sling properties.
Expand Down Expand Up @@ -264,6 +280,8 @@ func (s *Sling) getRequestBody() (body io.Reader, err error) {
if err != nil {
return nil, err
}
} else if s.body != nil {
body, err = s.body, nil
}
return body, nil
}
Expand Down
43 changes: 42 additions & 1 deletion sling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"math"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
)

Expand Down Expand Up @@ -319,6 +321,43 @@ func TestBodyFormSetter(t *testing.T) {

}

func TestBodySetter(t *testing.T) {
cases := []struct {
initial string
input string
expected string
}{
// an initial empty body is overriden by a set body
{"", "test", "test"},
// a initial set body is not overriden by an empty set one
{"test", "", "test"},
// an empty body is returned unaltered
{"", "", ""},
}
for _, c := range cases {
sling := New()
if c.initial == "" {
sling.body = nil
} else {
sling.body = ioutil.NopCloser(strings.NewReader(c.initial))
}
if c.input == "" {
sling.Body(nil)
} else {
sling.Body(ioutil.NopCloser(strings.NewReader(c.input)))
}

r, err := sling.getRequestBody()
if err == nil && r != nil {
buf := new(bytes.Buffer)
buf.ReadFrom(r)
if buf.String() != c.expected {
t.Errorf("expected %v, got %v from getRequestBody()", c.expected, sling.body)
}
}
}
}

func TestRequest_urlAndMethod(t *testing.T) {
cases := []struct {
sling *Sling
Expand Down Expand Up @@ -403,6 +442,8 @@ func TestRequest_body(t *testing.T) {
{New().BodyJSON(modelA).New().BodyForm(paramsB), "count=25&kind_name=recent", formContentType},
{New().BodyForm(paramsB).New().BodyJSON(nil), "count=25&kind_name=recent", formContentType},
{New().BodyJSON(modelA).New().BodyForm(nil), "{\"text\":\"note\",\"favorite_count\":12}\n", jsonContentType},
// Body
{New().Body(strings.NewReader("this-is-a-test")), "this-is-a-test", ""},
}
for _, c := range cases {
req, _ := c.sling.Request()
Expand All @@ -413,7 +454,7 @@ func TestRequest_body(t *testing.T) {
t.Errorf("expected Request.Body %s, got %s", c.expectedBody, value)
}
// Header Content-Type should be application/json
if actualHeader := req.Header.Get(contentType); actualHeader != c.expectedContentType {
if actualHeader := req.Header.Get(contentType); actualHeader != c.expectedContentType && c.expectedContentType != "" {
t.Errorf("Incorrect or missing header, expected %s, got %s", c.expectedContentType, actualHeader)
}
}
Expand Down