From ab1e34e1e6e13ba63ce806ea28bd1fceee27ea08 Mon Sep 17 00:00:00 2001 From: roc Date: Sun, 7 Aug 2022 13:51:58 +0800 Subject: [PATCH] Support Do API style(#137) --- client.go | 49 +++++++++++++++++++++++++++++++++++++++++ request.go | 42 ++++++++++++++++++++++++++--------- request_test.go | 10 +++++++++ request_wrapper.go | 6 +++++ request_wrapper_test.go | 1 + response.go | 18 +++++++++++++++ 6 files changed, 116 insertions(+), 10 deletions(-) diff --git a/client.go b/client.go index e9931aa7..7fde82a1 100644 --- a/client.go +++ b/client.go @@ -73,6 +73,55 @@ func (c *Client) R() *Request { } } +// Get create a new GET request. +func (c *Client) Get() *Request { + r := c.R() + r.Method = http.MethodGet + return r +} + +// Post create a new POST request. +func (c *Client) Post() *Request { + r := c.R() + r.Method = http.MethodPost + return r +} + +// Patch create a new PATCH request. +func (c *Client) Patch() *Request { + r := c.R() + r.Method = http.MethodPatch + return r +} + +// Delete create a new DELETE request. +func (c *Client) Delete() *Request { + r := c.R() + r.Method = http.MethodDelete + return r +} + +// Put create a new PUT request. +func (c *Client) Put() *Request { + r := c.R() + r.Method = http.MethodPut + return r +} + +// Head create a new HEAD request. +func (c *Client) Head() *Request { + r := c.R() + r.Method = http.MethodHead + return r +} + +// Options create a new OPTIONS request. +func (c *Client) Options() *Request { + r := c.R() + r.Method = http.MethodOptions + return r +} + // GetTransport return the underlying transport. func (c *Client) GetTransport() *Transport { return c.t diff --git a/request.go b/request.go index 89019b74..3adf28bf 100644 --- a/request.go +++ b/request.go @@ -141,6 +141,12 @@ func (r *Request) TraceInfo() TraceInfo { return ti } +// SetURL set the url for request. +func (r *Request) SetURL(url string) *Request { + r.RawURL = url + return r +} + // SetFormDataFromValues set the form data from url.Values, will not // been used if request method does not allow payload. func (r *Request) SetFormDataFromValues(data urlpkg.Values) *Request { @@ -443,21 +449,36 @@ func (r *Request) appendError(err error) { var errRetryableWithUnReplayableBody = errors.New("retryable request should not have unreplayable body (io.Reader)") -// Send fires http request and return the *Response which is always -// not nil, and the error is not nil if some error happens. -func (r *Request) Send(method, url string) (*Response, error) { +// Do fires http request and return the *Response which is always +// not nil, and the error is not nil if error occurs. +func (r *Request) Do() *Response { defer func() { r.responseReturnTime = time.Now() }() if r.error != nil { - return &Response{Request: r}, r.error + resp := &Response{Request: r} + resp.Err = r.error + return resp } if r.retryOption != nil && r.retryOption.MaxRetries > 0 && r.unReplayableBody != nil { // retryable request should not have unreplayable body - return &Response{Request: r}, errRetryableWithUnReplayableBody + resp := &Response{Request: r} + resp.Err = errRetryableWithUnReplayableBody + return resp } + resp, err := r.client.do(r) + if err != nil { + resp.Err = err + } + return resp +} + +// Send fires http request with specified method and url, returns the +// *Response which is always not nil, and the error is not nil if error occurs. +func (r *Request) Send(method, url string) (*Response, error) { r.Method = method r.RawURL = url - return r.client.do(r) + resp := r.Do() + return resp, resp.Err } // MustGet like Get, panic if error happens, should only be used to @@ -812,10 +833,11 @@ func (r *Request) SetRetryCount(count int) *Request { // SetRetryInterval sets the custom GetRetryIntervalFunc, you can use this to // implement your own backoff retry algorithm. // For example: -// req.SetRetryInterval(func(resp *req.Response, attempt int) time.Duration { -// sleep := 0.01 * math.Exp2(float64(attempt)) -// return time.Duration(math.Min(2, sleep)) * time.Second -// }) +// +// req.SetRetryInterval(func(resp *req.Response, attempt int) time.Duration { +// sleep := 0.01 * math.Exp2(float64(attempt)) +// return time.Duration(math.Min(2, sleep)) * time.Second +// }) func (r *Request) SetRetryInterval(getRetryIntervalFunc GetRetryIntervalFunc) *Request { r.getRetryOption().GetRetryInterval = getRetryIntervalFunc return r diff --git a/request_test.go b/request_test.go index 1bf6946c..624c6f47 100644 --- a/request_test.go +++ b/request_test.go @@ -359,6 +359,16 @@ func TestSetBodyMarshal(t *testing.T) { } } +func TestDoAPIStyle(t *testing.T) { + c := tc() + user := &UserInfo{} + url := "/search?username=imroc&type=json" + + err := c.Get().SetURL(url).Do().Into(user) + tests.AssertEqual(t, true, err == nil) + tests.AssertEqual(t, "imroc", user.Username) +} + func TestSetResult(t *testing.T) { c := tc() var user *UserInfo diff --git a/request_wrapper.go b/request_wrapper.go index e496cdc4..a64c2c25 100644 --- a/request_wrapper.go +++ b/request_wrapper.go @@ -8,6 +8,12 @@ import ( "time" ) +// SetURL is a global wrapper methods which delegated +// to the default client, create a request and SetURL for request. +func SetURL(url string) *Request { + return defaultClient.R().SetURL(url) +} + // SetFormDataFromValues is a global wrapper methods which delegated // to the default client, create a request and SetFormDataFromValues for request. func SetFormDataFromValues(data url.Values) *Request { diff --git a/request_wrapper_test.go b/request_wrapper_test.go index 4c74cbe9..91a244a6 100644 --- a/request_wrapper_test.go +++ b/request_wrapper_test.go @@ -40,6 +40,7 @@ func TestGlobalWrapperForRequestSettings(t *testing.T) { SetPathParam("test", "test"), SetPathParams(map[string]string{"test": "test"}), SetFormData(map[string]string{"test": "test"}), + SetURL(""), SetFormDataFromValues(nil), SetContentType(header.JsonContentType), AddRetryCondition(func(rep *Response, err error) bool { diff --git a/response.go b/response.go index 76a2dcc4..9695eeed 100644 --- a/response.go +++ b/response.go @@ -85,6 +85,9 @@ func (r *Response) setReceivedAt() { // UnmarshalJson unmarshals JSON response body into the specified object. func (r *Response) UnmarshalJson(v interface{}) error { + if r.Err != nil { + return r.Err + } b, err := r.ToBytes() if err != nil { return err @@ -94,6 +97,9 @@ func (r *Response) UnmarshalJson(v interface{}) error { // UnmarshalXml unmarshals XML response body into the specified object. func (r *Response) UnmarshalXml(v interface{}) error { + if r.Err != nil { + return r.Err + } b, err := r.ToBytes() if err != nil { return err @@ -104,6 +110,9 @@ func (r *Response) UnmarshalXml(v interface{}) error { // Unmarshal unmarshals response body into the specified object according // to response `Content-Type`. func (r *Response) Unmarshal(v interface{}) error { + if r.Err != nil { + return r.Err + } contentType := r.Header.Get("Content-Type") if strings.Contains(contentType, "json") { return r.UnmarshalJson(v) @@ -113,6 +122,12 @@ func (r *Response) Unmarshal(v interface{}) error { return r.UnmarshalJson(v) } +// Into unmarshals response body into the specified object according +// to response `Content-Type`. +func (r *Response) Into(v interface{}) error { + return r.Unmarshal(v) +} + // Bytes return the response body as []bytes that hava already been read, could be // nil if not read, the following cases are already read: // 1. `Request.SetResult` or `Request.SetError` is called. @@ -139,6 +154,9 @@ func (r *Response) ToString() (string, error) { // ToBytes returns the response body as []byte, read body if not have been read. func (r *Response) ToBytes() ([]byte, error) { + if r.Err != nil { + return nil, r.Err + } if r.body != nil { return r.body, nil }