diff --git a/go.mod b/go.mod index da2a83a..1ea7a75 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b github.com/smartystreets/goconvey v1.7.2 // indirect + github.com/spf13/cast v1.4.1 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f moul.io/http2curl v1.0.0 ) diff --git a/go.sum b/go.sum index 8288919..f77ede7 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b h1:1XqENn2YoYZd6w3Awx+7oa+aR87DFIZJFLF2n1IojA0= github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= @@ -6,11 +8,17 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= diff --git a/gorequest.go b/gorequest.go index 4fabfd6..f9c2ce0 100644 --- a/gorequest.go +++ b/gorequest.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "github.com/spf13/cast" "golang.org/x/net/publicsuffix" "moul.io/http2curl" ) @@ -299,6 +300,58 @@ func (s *SuperAgent) Set(param string, value string) *SuperAgent { return s } +// SetHeaders is used for setting all your headers with the use of a map or a struct. +// It uses AppendHeader() method so it allows for multiple values of the same header +// Example. To set the following struct as headers, simply do +// +// headers := apiHeaders{Accept: "application/json", Content-Type: "text/html", X-Frame-Options: "deny"} +// gorequest.New(). +// Post("apiEndPoint"). +// Set(headers). +// End() +func (s *SuperAgent) SetHeaders(headers interface{}) *SuperAgent { + switch v := reflect.ValueOf(headers); v.Kind() { + case reflect.Struct: + s.setHeadersStruct(v.Interface()) + case reflect.Map: + s.setHeadersMap(v.Interface()) + default: + return s + } + return s +} + +func (s *SuperAgent) setHeadersMap(content interface{}) *SuperAgent { + return s.setHeadersStruct(content) +} + +// SendStruct (similar to SendString) returns SuperAgent's itself for any next chain and takes content interface{} as a parameter. +// Its duty is to transform interface{} (implicitly always a struct) into s.Data (map[string]interface{}) which later changes into appropriate format such as json, form, text, etc. in the End() func. +func (s *SuperAgent) setHeadersStruct(content interface{}) *SuperAgent { + if marshalContent, err := json.Marshal(content); err != nil { + s.Errors = append(s.Errors, err) + } else { + var val map[string]interface{} + d := json.NewDecoder(bytes.NewBuffer(marshalContent)) + d.UseNumber() + if err := d.Decode(&val); err != nil { + s.Errors = append(s.Errors, err) + } else { + for k, v := range val { + strValue, err := cast.ToStringE(v) + if err != nil { + // TODO: log err? + continue + } + + s.AppendHeader(k, strValue) + } + + } + } + return s +} + // AppendHeader is used for setting headers with multiple values, // Example. To set `Accept` as `application/json, text/plain` // diff --git a/gorequest_test.go b/gorequest_test.go index b710e50..90a209f 100644 --- a/gorequest_test.go +++ b/gorequest_test.go @@ -2609,6 +2609,64 @@ func TestSetDebugByEnvironmentVar(t *testing.T) { } } +func TestSetHeaders(t *testing.T) { + text := "hi" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // check method is PATCH before going to check other features + if r.Method != POST { + t.Errorf("Expected method %q; got %q", POST, r.Method) + } + if r.Header == nil { + t.Error("Expected non-nil request Header") + } + if r.Header.Get("Connection") != "keep-Alive" { + t.Error("Expected Header Connection -> keep-Alive", "| but got", r.Header.Get("Connection")) + } + if r.Header.Get("Date") != "Fri, 22 Jan 2010 04:00:00 GMT" { + t.Error("Expected Header Date -> Fri, 22 Jan 2010 04:00:00 GMT", "| but got", r.Header.Get("Date")) + } + expectedAccepts := []string{"application/json", "text/plain"} + if strings.Join(r.Header["Accept"], ", ") != strings.Join(expectedAccepts, ", ") { + t.Error("Expected Header Accept -> ", expectedAccepts, "| but got", r.Header["Accept"]) + } + + defer r.Body.Close() + body, _ := ioutil.ReadAll(r.Body) + if string(body) != text { + t.Error(`Expected text `, text, "| but got", string(body)) + } + })) + + defer ts.Close() + + type headers struct { + Accept string + Connection string + Date string + } + + headersStruct := headers{Accept: "application/json", Connection: "keep-Alive", Date: "Fri, 22 Jan 2010 04:00:00 GMT"} + headersMap := map[string]string{ + "Accept": "application/json", + "Connection": "keep-Alive", + "Date": "Fri, 22 Jan 2010 04:00:00 GMT", + } + + New().Post(ts.URL). + SetHeaders(headersStruct). + AppendHeader("Accept", "text/plain"). + Type("text"). + Send(text). + End() + + New().Post(ts.URL). + SetHeaders(headersMap). + AppendHeader("Accept", "text/plain"). + Type("text"). + Send(text). + End() + func TestContext(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // check method is GET before going to check other features