diff --git a/middleware.go b/middleware.go index 7b26f214..21e6056f 100644 --- a/middleware.go +++ b/middleware.go @@ -209,9 +209,9 @@ func requestLogger(c *Client, r *Request) error { return nil } -// +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Response Middleware(s) -// +//___________________________________ func responseLogger(c *Client, res *Response) error { if c.Debug { @@ -239,7 +239,7 @@ func responseLogger(c *Client, res *Response) error { func parseResponseBody(c *Client, res *Response) (err error) { // Handles only JSON or XML content type - ct := res.Header().Get(hdrContentTypeKey) + ct := firstNonEmpty(res.Header().Get(hdrContentTypeKey), res.Request.fallbackContentType) if IsJSONType(ct) || IsXMLType(ct) { // Considered as Result if res.StatusCode() > 199 && res.StatusCode() < 300 { diff --git a/request.go b/request.go index 4095a997..5cb89b66 100644 --- a/request.go +++ b/request.go @@ -354,9 +354,16 @@ func (r *Request) SetDoNotParseResponse(parse bool) *Request { return r } -// +// ExpectContentType method allows to provide fallback `Content-Type` for automatic unmarshalling +// when `Content-Type` response header is unavailable. +func (r *Request) ExpectContentType(contentType string) *Request { + r.fallbackContentType = contentType + return r +} + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // HTTP verb method starts here -// +//___________________________________ // Get method does GET HTTP request. It's defined in section 4.3.1 of RFC7231. func (r *Request) Get(url string) (*Response, error) { @@ -448,6 +455,10 @@ func (r *Request) Execute(method, url string) (*Response, error) { return resp, err } +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Unexported methods +//___________________________________ + func (r *Request) fmtBodyString() (body string) { body = "***** NO CONTENT *****" if isPayloadSupported(r.Method, r.client.AllowGetMethodPayload) { diff --git a/request16.go b/request16.go index 359d714d..dd41e4be 100644 --- a/request16.go +++ b/request16.go @@ -35,15 +35,16 @@ type Request struct { RawRequest *http.Request SRV *SRVRecord - client *Client - bodyBuf *bytes.Buffer - isMultiPart bool - isFormData bool - setContentLength bool - isSaveResponse bool - outputFile string - multipartFiles []*File - notParseResponse bool + client *Client + bodyBuf *bytes.Buffer + isMultiPart bool + isFormData bool + setContentLength bool + isSaveResponse bool + outputFile string + multipartFiles []*File + notParseResponse bool + fallbackContentType string } func (r *Request) addContextIfAvailable() { diff --git a/request17.go b/request17.go index 64c0f076..fe00515d 100644 --- a/request17.go +++ b/request17.go @@ -36,16 +36,17 @@ type Request struct { RawRequest *http.Request SRV *SRVRecord - client *Client - bodyBuf *bytes.Buffer - isMultiPart bool - isFormData bool - setContentLength bool - isSaveResponse bool - outputFile string - multipartFiles []*File - notParseResponse bool - ctx context.Context + client *Client + bodyBuf *bytes.Buffer + isMultiPart bool + isFormData bool + setContentLength bool + isSaveResponse bool + outputFile string + multipartFiles []*File + notParseResponse bool + ctx context.Context + fallbackContentType string } // SetContext method sets the context.Context for current Request. It allows diff --git a/resty_test.go b/resty_test.go index 9d38c511..9195d780 100644 --- a/resty_test.go +++ b/resty_test.go @@ -1111,7 +1111,6 @@ func TestContextInternal(t *testing.T) { assertError(t, err) assertEqual(t, http.StatusOK, resp.StatusCode()) - } func TestSRV(t *testing.T) { @@ -1193,6 +1192,28 @@ func TestRequestDoNotParseResponse(t *testing.T) { SetDoNotParseResponse(false) } +type noCtTest struct { + Response string `json:"response"` +} + +func TestRequestExpectContentTypeTest(t *testing.T) { + ts := createGenServer(t) + defer ts.Close() + + c := dc() + resp, err := c.R(). + SetResult(noCtTest{}). + ExpectContentType("application/json"). + Get(ts.URL + "/json-no-set") + + assertError(t, err) + assertEqual(t, http.StatusOK, resp.StatusCode()) + assertNotNil(t, resp.Result()) + assertEqual(t, "json response no content type set", resp.Result().(*noCtTest).Response) + + assertEqual(t, "", firstNonEmpty("", "")) +} + //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Testing Unexported methods //___________________________________ @@ -1541,6 +1562,16 @@ func createGenServer(t *testing.T) *httptest.Server { t.Logf("Method: %v", r.Method) t.Logf("Path: %v", r.URL.Path) + if r.Method == MethodGet { + if r.URL.Path == "/json-no-set" { + // Set empty header value for testing, since Go server sets to + // text/plain; charset=utf-8 + w.Header().Set(hdrContentTypeKey, "") + _, _ = w.Write([]byte(`{"response":"json response no content type set"}`)) + } + return + } + if r.Method == MethodPut { if r.URL.Path == "/plaintext" { _, _ = w.Write([]byte("TestPut: plain text response")) @@ -1551,6 +1582,7 @@ func createGenServer(t *testing.T) *httptest.Server { w.Header().Set(hdrContentTypeKey, "application/xml") _, _ = w.Write([]byte(`XML response`)) } + return } if r.Method == MethodOptions && r.URL.Path == "/options" { @@ -1558,10 +1590,12 @@ func createGenServer(t *testing.T) *httptest.Server { w.Header().Set("Access-Control-Allow-Methods", "PUT, PATCH") w.Header().Set("Access-Control-Expose-Headers", "x-go-resty-id") w.WriteHeader(http.StatusOK) + return } if r.Method == MethodPatch && r.URL.Path == "/patch" { w.WriteHeader(http.StatusOK) + return } }) diff --git a/util.go b/util.go new file mode 100644 index 00000000..fccec8ce --- /dev/null +++ b/util.go @@ -0,0 +1,14 @@ +// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved. +// resty source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package resty + +func firstNonEmpty(v ...string) string { + for _, s := range v { + if !IsStringEmpty(s) { + return s + } + } + return "" +}