Skip to content

Commit

Permalink
Custom content type and data for multipart request #130 (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
Asker80 authored and jeevatkm committed Feb 24, 2018
1 parent cf9fbbb commit e008fe0
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 18 deletions.
35 changes: 22 additions & 13 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,19 +300,20 @@ func (c *Client) SetAuthToken(token string) *Client {
// R method creates a request instance, its used for Get, Post, Put, Delete, Patch, Head and Options.
func (c *Client) R() *Request {
r := &Request{
URL: "",
Method: "",
QueryParam: url.Values{},
FormData: url.Values{},
Header: http.Header{},
Body: nil,
Result: nil,
Error: nil,
RawRequest: nil,
client: c,
bodyBuf: nil,
multipartFiles: []*File{},
pathParams: make(map[string]string),
URL: "",
Method: "",
QueryParam: url.Values{},
FormData: url.Values{},
Header: http.Header{},
Body: nil,
Result: nil,
Error: nil,
RawRequest: nil,
client: c,
bodyBuf: nil,
multipartFiles: []*File{},
multipartFields: []*multipartField{},
pathParams: make(map[string]string),
}

return r
Expand Down Expand Up @@ -865,3 +866,11 @@ type File struct {
func (f *File) String() string {
return fmt.Sprintf("ParamName: %v; FileName: %v", f.ParamName, f.Name)
}

// multipartField represent custom data part for multipart request
type multipartField struct {
Param string
FileName string
ContentType string
io.Reader
}
9 changes: 9 additions & 0 deletions middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,15 @@ func handleMultipart(c *Client, r *Request) (err error) {
}
}

// GitHub #130 adding multipart field support with content type
if len(r.multipartFields) > 0 {
for _, mf := range r.multipartFields {
if err = addMultipartFormField(w, mf); err != nil {
return
}
}
}

r.Header.Set(hdrContentTypeKey, w.FormDataContentType())
err = w.Close()

Expand Down
14 changes: 14 additions & 0 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,20 @@ func (r *Request) SetFileReader(param, fileName string, reader io.Reader) *Reque
return r
}

// SetMultipartField method is to set custom data using io.Reader for multipart upload.
func (r *Request) SetMultipartField(param, fileName, contentType string, reader io.Reader) *Request {
r.isMultiPart = true

r.multipartFields = append(r.multipartFields, &multipartField{
Param: param,
FileName: fileName,
ContentType: contentType,
Reader: reader,
})

return r
}

// SetContentLength method sets the HTTP header `Content-Length` value for current request.
// By default go-resty won't set `Content-Length`. Also you have an option to enable for every
// request. See `resty.SetContentLength`
Expand Down
1 change: 1 addition & 0 deletions request16.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Request struct {
isSaveResponse bool
outputFile string
multipartFiles []*File
multipartFields []*multipartField
notParseResponse bool
fallbackContentType string
pathParams map[string]string
Expand Down
1 change: 1 addition & 0 deletions request17.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type Request struct {
isSaveResponse bool
outputFile string
multipartFiles []*File
multipartFields []*multipartField
notParseResponse bool
ctx context.Context
fallbackContentType string
Expand Down
19 changes: 19 additions & 0 deletions request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,25 @@ func TestMultiPartUploadFileNotOnGetOrDelete(t *testing.T) {
assertEqual(t, "multipart content is not allowed in HTTP verb [DELETE]", err.Error())
}

func TestMultiPartMultipartField(t *testing.T) {
ts := createFormPostServer(t)
defer ts.Close()
defer cleanupFiles("test-data/upload")

jsonBytes := []byte(`{"input": {"name": "Uploaded document", "_filename" : ["file.txt"]}}`)

resp, err := dclr().
SetFormData(map[string]string{"first_name": "Jeevanandam", "last_name": "M"}).
SetMultipartField("uploadManifest", "upload-file.json", "application/json", bytes.NewReader(jsonBytes)).
Post(ts.URL + "/upload")

responseStr := resp.String()

assertError(t, err)
assertEqual(t, http.StatusOK, resp.StatusCode())
assertEqual(t, true, strings.Contains(responseStr, "upload-file.json"))
}

func TestGetWithCookie(t *testing.T) {
ts := createGetServer(t)
defer ts.Close()
Expand Down
24 changes: 19 additions & 5 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}

func createMultipartHeader(param, fileName, contentType string) textproto.MIMEHeader {
hdr := make(textproto.MIMEHeader)
hdr.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
escapeQuotes(param), escapeQuotes(fileName)))
hdr.Set("Content-Type", contentType)
return hdr
}

func addMultipartFormField(w *multipart.Writer, mf *multipartField) error {
partWriter, err := w.CreatePart(createMultipartHeader(mf.Param, mf.FileName, mf.ContentType))
if err != nil {
return err
}

_, err = io.Copy(partWriter, mf.Reader)
return err
}

func writeMultipartFormFile(w *multipart.Writer, fieldName, fileName string, r io.Reader) error {
// Auto detect actual multipart content type
cbuf := make([]byte, 512)
Expand All @@ -115,11 +133,7 @@ func writeMultipartFormFile(w *multipart.Writer, fieldName, fileName string, r i
return err
}

h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
escapeQuotes(fieldName), escapeQuotes(fileName)))
h.Set("Content-Type", http.DetectContentType(cbuf))
partWriter, err := w.CreatePart(h)
partWriter, err := w.CreatePart(createMultipartHeader(fieldName, fileName, http.DetectContentType(cbuf)))
if err != nil {
return err
}
Expand Down

0 comments on commit e008fe0

Please sign in to comment.