Skip to content
Merged
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
24 changes: 13 additions & 11 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1165,29 +1165,31 @@ func (c *Client) SetTimeout(timeout time.Duration) *Client {
return c
}

// Error method returns the global or client common `Error` object type registered in the Resty.
func (c *Client) Error() reflect.Type {
// ResultError method returns the global or client common `ResultError` object
// type registered in the client instance.
func (c *Client) ResultError() reflect.Type {
c.lock.RLock()
defer c.lock.RUnlock()
return c.errorType
}

// SetError method registers the global or client common `Error` object into Resty.
// It is used for automatic unmarshalling if the response status code is greater than 399 and
// content type is JSON or XML. It can be a pointer or a non-pointer.
// SetResultError method registers the global or client common `ResultError`
// object type into the client instance. It is used for automatic unmarshalling if
// the response status code is greater than 399 and the content type is JSON or XML.
// It can be a pointer or a non-pointer.
//
// client.SetError(&Error{})
// client.SetResultError(&LoginErrorResponse{})
// // OR
// client.SetError(Error{})
func (c *Client) SetError(v any) *Client {
// client.SetResultError(LoginErrorResponse{})
func (c *Client) SetResultError(v any) *Client {
c.lock.Lock()
defer c.lock.Unlock()
c.errorType = inferType(v)
return c
}

func (c *Client) newErrorInterface() any {
e := c.Error()
e := c.ResultError()
if e == nil {
return e
}
Expand Down Expand Up @@ -2300,11 +2302,11 @@ func (c *Client) execute(req *Request) (*Response, error) {
// Apply Response middleware
for _, f := range c.responseMiddlewares() {
if err = f(c, response); err != nil {
response.Err = wrapErrors(err, response.Err)
response.CascadeError = wrapErrors(err, response.CascadeError)
}
}

err = response.Err
err = response.CascadeError
return response, err
}

Expand Down
14 changes: 7 additions & 7 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ func TestNewWithLocalAddr(t *testing.T) {
assertEqual(t, "TestGet: text response", resp.String())
}

func TestClientOnResponseError(t *testing.T) {
func TestClientOnResponseFailure(t *testing.T) {
tests := []struct {
name string
setup func(*Client)
Expand All @@ -1050,13 +1050,13 @@ func TestClientOnResponseError(t *testing.T) {
name: "successful_request",
},
{
name: "http_status_error",
name: "http_status_failure",
setup: func(client *Client) {
client.SetAuthToken("BAD")
},
},
{
name: "before_request_error",
name: "before_request_failure",
setup: func(client *Client) {
client.AddRequestMiddleware(func(client *Client, request *Request) error {
return fmt.Errorf("before request")
Expand All @@ -1065,7 +1065,7 @@ func TestClientOnResponseError(t *testing.T) {
isError: true,
},
{
name: "before_request_error_retry",
name: "before_request_failure_retry",
setup: func(client *Client) {
client.SetRetryCount(3).AddRequestMiddleware(func(client *Client, request *Request) error {
return fmt.Errorf("before request")
Expand All @@ -1074,7 +1074,7 @@ func TestClientOnResponseError(t *testing.T) {
isError: true,
},
{
name: "after_response_error",
name: "after_response_failure",
setup: func(client *Client) {
client.AddResponseMiddleware(func(client *Client, response *Response) error {
return fmt.Errorf("after response")
Expand All @@ -1084,7 +1084,7 @@ func TestClientOnResponseError(t *testing.T) {
hasResponse: true,
},
{
name: "after_response_error_retry",
name: "after_response_failure_retry",
setup: func(client *Client) {
client.SetRetryCount(3).AddResponseMiddleware(func(client *Client, response *Response) error {
return fmt.Errorf("after response")
Expand Down Expand Up @@ -1152,7 +1152,7 @@ func TestClientOnResponseError(t *testing.T) {
if err != nil {
return true
}
return response.IsError()
return response.IsStatusFailure()
}).
OnError(func(r *Request, err error) {
assertErrorHook(r, err)
Expand Down
10 changes: 5 additions & 5 deletions middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,9 @@ func handleRequestBody(c *Client, r *Request) error {

// AutoParseResponseMiddleware method is used to parse the response body automatically
// based on registered HTTP response `Content-Type` decoder, see [Client.AddContentTypeDecoder];
// if [Request.SetResult], [Request.SetError], or [Client.SetError] is used
// if [Request.SetResult], [Request.SetResultError], or [Client.SetResultError] is used
func AutoParseResponseMiddleware(c *Client, res *Response) (err error) {
if res.Err != nil || res.Request.DoNotParseResponse {
if res.CascadeError != nil || res.Request.DoNotParseResponse {
return // move on
}

Expand All @@ -505,7 +505,7 @@ func AutoParseResponseMiddleware(c *Client, res *Response) (err error) {
}

// HTTP status code > 199 and < 300, considered as Result
if res.IsSuccess() && res.Request.Result != nil {
if res.IsStatusSuccess() && res.Request.Result != nil {
res.Request.Error = nil
defer closeq(res.Body)
err = decFunc(res.Body, res.Request.Result)
Expand All @@ -514,7 +514,7 @@ func AutoParseResponseMiddleware(c *Client, res *Response) (err error) {
}

// HTTP status code > 399, considered as Error
if res.IsError() {
if res.IsStatusFailure() {
// global error type registered at client-instance
if res.Request.Error == nil {
res.Request.Error = c.newErrorInterface()
Expand All @@ -539,7 +539,7 @@ var hostnameReplacer = strings.NewReplacer(":", "_", ".", "_")
// - Content-Disposition header
// - Request URL using [path.Base]
func SaveToFileResponseMiddleware(c *Client, res *Response) error {
if res.Err != nil || !res.Request.IsSaveResponse {
if res.CascadeError != nil || !res.Request.IsSaveResponse {
return nil
}

Expand Down
24 changes: 13 additions & 11 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,9 @@ func (r *Request) SetBody(body any) *Request {
return r
}

// SetResult method is to register the response `Result` object for automatic
// SetResult method registers the response `Result` object type for automatic
// unmarshalling of the HTTP response if the response status code is
// between 200 and 299, and the content type is JSON or XML.
// between 200 and 299, and the content type is either JSON or XML.
//
// Note: [Request.SetResult] input can be a pointer or non-pointer.
//
Expand All @@ -427,21 +427,23 @@ func (r *Request) SetResult(v any) *Request {
return r
}

// SetError method is to register the request `Error` object for automatic unmarshalling for the request,
// if the response status code is greater than 399 and the content type is either JSON or XML.
// SetResultError method registers the response `ResultError` object type for automatic
// unmarshalling for the request, if the response status code is greater than 399 and
// the content type is either JSON or XML.
//
// NOTE: [Request.SetError] input can be a pointer or non-pointer.
// NOTE: [Request.SetResultError] input can be a pointer or non-pointer.
//
// client.R().SetError(&AuthError{})
// client.R().SetResultError(&AuthError{})
// // OR
// client.R().SetError(AuthError{})
// client.R().SetResultError(AuthError{})
//
// Accessing an error value from response instance.
// Accessing an unmarshalled error object from response instance.
//
// response.Error().(*AuthError)
// response.ResultError().(*AuthError)
//
// If this request Error object is nil, Resty will use the client-level error object Type if it is set.
func (r *Request) SetError(err any) *Request {
// If this request ResultError object is nil, it will use the client-level error object
// type if it is set.
func (r *Request) SetResultError(err any) *Request {
r.Error = getPointer(err)
return r
}
Expand Down
24 changes: 12 additions & 12 deletions request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,17 +281,17 @@ func TestPostJSONStructInvalidLogin(t *testing.T) {
resp, err := c.R().
SetHeader(hdrContentTypeKey, "application/json; charset=utf-8").
SetBody(credentials{Username: "testuser", Password: "testpass1"}).
SetError(AuthError{}).
SetResultError(AuthError{}).
SetJSONEscapeHTML(false).
Post(ts.URL + "/login")

assertError(t, err)
assertEqual(t, http.StatusUnauthorized, resp.StatusCode())

authError := resp.Error().(*AuthError)
authError := resp.ResultError().(*AuthError)
assertEqual(t, "unauthorized", authError.ID)
assertEqual(t, "Invalid credentials", authError.Message)
t.Logf("Result Error: %q", resp.Error().(*AuthError))
t.Logf("Result Error: %q", resp.ResultError().(*AuthError))

logResponse(t, resp)
}
Expand All @@ -304,16 +304,16 @@ func TestPostJSONErrorRFC7807(t *testing.T) {
resp, err := c.R().
SetHeader(hdrContentTypeKey, "application/json; charset=utf-8").
SetBody(credentials{Username: "testuser", Password: "testpass1"}).
SetError(AuthError{}).
SetResultError(AuthError{}).
Post(ts.URL + "/login?ct=problem")

assertError(t, err)
assertEqual(t, http.StatusUnauthorized, resp.StatusCode())

authError := resp.Error().(*AuthError)
authError := resp.ResultError().(*AuthError)
assertEqual(t, "unauthorized", authError.ID)
assertEqual(t, "Invalid credentials", authError.Message)
t.Logf("Result Error: %q", resp.Error().(*AuthError))
t.Logf("Result Error: %q", resp.ResultError().(*AuthError))

logResponse(t, resp)
}
Expand Down Expand Up @@ -513,7 +513,7 @@ func TestPostXMLStructInvalidLogin(t *testing.T) {
defer ts.Close()

c := dcnl()
c.SetError(&AuthError{})
c.SetResultError(&AuthError{})

resp, err := c.R().
SetHeader(hdrContentTypeKey, "application/xml").
Expand All @@ -524,7 +524,7 @@ func TestPostXMLStructInvalidLogin(t *testing.T) {
assertEqual(t, http.StatusUnauthorized, resp.StatusCode())
assertEqual(t, resp.Header().Get("Www-Authenticate"), "Protected Realm")

t.Logf("Result Error: %q", resp.Error().(*AuthError))
t.Logf("Result Error: %q", resp.ResultError().(*AuthError))

logResponse(t, resp)
}
Expand Down Expand Up @@ -633,7 +633,7 @@ func TestRequestBasicAuthFail(t *testing.T) {

c := dcnl()
c.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
SetError(AuthError{})
SetResultError(AuthError{})

resp, err := c.R().
SetBasicAuth("myuser", "basicauth1").
Expand All @@ -642,7 +642,7 @@ func TestRequestBasicAuthFail(t *testing.T) {
assertError(t, err)
assertEqual(t, http.StatusUnauthorized, resp.StatusCode())

t.Logf("Result Error: %q", resp.Error().(*AuthError))
t.Logf("Result Error: %q", resp.ResultError().(*AuthError))
logResponse(t, resp)
}

Expand Down Expand Up @@ -1700,7 +1700,7 @@ func TestNotFoundWithError(t *testing.T) {

resp, err := dcnl().R().
SetHeader(hdrContentTypeKey, "application/json").
SetError(&httpError).
SetResultError(&httpError).
Get(ts.URL + "/not-found-with-error")

assertError(t, err)
Expand All @@ -1720,7 +1720,7 @@ func TestNotFoundWithoutError(t *testing.T) {

c := dcnl().outputLogTo(os.Stdout)
resp, err := c.R().
SetError(&httpError).
SetResultError(&httpError).
SetHeader(hdrContentTypeKey, "application/json").
Get(ts.URL + "/not-found-no-error")

Expand Down
60 changes: 49 additions & 11 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ type Response struct {
RawResponse *http.Response
IsRead bool

// Err field used to cascade the response middleware error
// in the chain
Err error
// CascadeError field used to cascade the response processing and
// middleware execution errors
CascadeError error

bodyBytes []byte
size int64
Expand Down Expand Up @@ -63,17 +63,51 @@ func (r *Response) Proto() string {
return r.RawResponse.Proto
}

// Result method returns the response value as an object if it has one
// Result method returns the unmarshalled result response object if it exists,
// otherwise nil.
//
// client := resty.New()
// defer client.Close()
//
// res, err := client.R().
// SetBody(User{
// Username: "testuser",
// Password: "testpass",
// }).
// SetResult(&LoginResponse{}). // or SetResult(LoginResponse{}).
// SetResultError(&LoginErrorResponse{}). // or SetResultError(LoginErrorResponse{}).
// Post("https://myapp.com/login")
//
// fmt.Println(err, res)
// fmt.Println(res.Result().(*LoginResponse))
// fmt.Println(res.ResultError().(*LoginErrorResponse))
//
// See [Request.SetResult]
func (r *Response) Result() any {
return r.Request.Result
}

// Error method returns the error object if it has one
// ResultError method returns the unmarshalled result error object if it exists,
// otherwise nil.
//
// client := resty.New()
// defer client.Close()
//
// See [Request.SetError], [Client.SetError]
func (r *Response) Error() any {
// res, err := client.R().
// SetBody(User{
// Username: "testuser",
// Password: "testpass",
// }).
// SetResult(&LoginResponse{}). // or SetResult(LoginResponse{}).
// SetResultError(&LoginErrorResponse{}). // or SetResultError(LoginErrorResponse{}).
// Post("https://myapp.com/login")
//
// fmt.Println(err, res)
// fmt.Println(res.Result().(*LoginResponse))
// fmt.Println(res.ResultError().(*LoginErrorResponse))
//
// See [Request.SetResultError], [Client.SetResultError]
func (r *Response) ResultError() any {
return r.Request.Error
}

Expand Down Expand Up @@ -143,13 +177,17 @@ func (r *Response) Size() int64 {
return r.size
}

// IsSuccess method returns true if HTTP status `code >= 200 and <= 299` otherwise false.
func (r *Response) IsSuccess() bool {
// IsStatusSuccess method returns true if HTTP status `code >= 200 and <= 299` otherwise false.
//
// Example: 200, 201, 204, etc.
func (r *Response) IsStatusSuccess() bool {
return r.StatusCode() > 199 && r.StatusCode() < 300
}

// IsError method returns true if HTTP status `code >= 400` otherwise false.
func (r *Response) IsError() bool {
// IsStatusFailure method returns true if HTTP status `code >= 400` otherwise false.
//
// Example: 400, 500, etc.
func (r *Response) IsStatusFailure() bool {
return r.StatusCode() > 399
}

Expand Down
Loading