Skip to content

Commit cfff250

Browse files
committedOct 10, 2024
Support ordered form data (imroc#391)
1 parent 8d3a134 commit cfff250

File tree

2 files changed

+67
-19
lines changed

2 files changed

+67
-19
lines changed
 

‎middleware.go

+46-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package req
22

33
import (
44
"bytes"
5+
"errors"
56
"io"
67
"mime/multipart"
78
"net/http"
@@ -124,17 +125,30 @@ func writeMultipartFormFile(w *multipart.Writer, file *FileUpload, r *Request) e
124125

125126
func writeMultiPart(r *Request, w *multipart.Writer) {
126127
defer w.Close() // close multipart to write tailer boundary
127-
for k, vs := range r.FormData {
128-
for _, v := range vs {
129-
w.WriteField(k, v)
128+
if len(r.FormData) > 0 {
129+
for k, vs := range r.FormData {
130+
for _, v := range vs {
131+
w.WriteField(k, v)
132+
}
133+
}
134+
} else if len(r.OrderedFormData) > 0 {
135+
if len(r.OrderedFormData)%2 != 0 {
136+
r.error = errBadOrderedFormData
137+
return
138+
}
139+
maxIndex := len(r.OrderedFormData) - 2
140+
for i := 0; i <= maxIndex; i += 2 {
141+
key := r.OrderedFormData[i]
142+
value := r.OrderedFormData[i+1]
143+
w.WriteField(key, value)
130144
}
131145
}
132146
for _, file := range r.uploadFiles {
133147
writeMultipartFormFile(w, file, r)
134148
}
135149
}
136150

137-
func handleMultiPart(c *Client, r *Request) (err error) {
151+
func handleMultiPart(r *Request) (err error) {
138152
if r.forceChunkedEncoding {
139153
pr, pw := io.Pipe()
140154
r.GetBody = func() (io.ReadCloser, error) {
@@ -164,6 +178,29 @@ func handleFormData(r *Request) {
164178
r.SetBodyBytes([]byte(r.FormData.Encode()))
165179
}
166180

181+
var errBadOrderedFormData = errors.New("bad ordered form data, the number of key-value pairs should be an even number")
182+
183+
func handleOrderedFormData(r *Request) {
184+
r.SetContentType(header.FormContentType)
185+
if len(r.OrderedFormData)%2 != 0 {
186+
r.error = errBadOrderedFormData
187+
return
188+
}
189+
maxIndex := len(r.OrderedFormData) - 2
190+
var buf strings.Builder
191+
for i := 0; i <= maxIndex; i += 2 {
192+
key := r.OrderedFormData[i]
193+
value := r.OrderedFormData[i+1]
194+
if buf.Len() > 0 {
195+
buf.WriteByte('&')
196+
}
197+
buf.WriteString(url.QueryEscape(key))
198+
buf.WriteByte('=')
199+
buf.WriteString(url.QueryEscape(value))
200+
}
201+
r.SetBodyString(buf.String())
202+
}
203+
167204
func handleMarshalBody(c *Client, r *Request) error {
168205
ct := ""
169206
if r.Headers != nil {
@@ -205,16 +242,20 @@ func parseRequestBody(c *Client, r *Request) (err error) {
205242
}
206243
// handle multipart
207244
if r.isMultiPart {
208-
return handleMultiPart(c, r)
245+
return handleMultiPart(r)
209246
}
210247

211248
// handle form data
212249
if len(c.FormData) > 0 {
213250
r.SetFormDataFromValues(c.FormData)
214251
}
252+
215253
if len(r.FormData) > 0 {
216254
handleFormData(r)
217255
return
256+
} else if len(r.OrderedFormData) > 0 {
257+
handleOrderedFormData(r)
258+
return
218259
}
219260

220261
// handle marshal body

‎request.go

+21-14
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,21 @@ import (
2525
// req client. Request provides lots of chainable settings which can
2626
// override client level settings.
2727
type Request struct {
28-
PathParams map[string]string
29-
QueryParams urlpkg.Values
30-
FormData urlpkg.Values
31-
Headers http.Header
32-
Cookies []*http.Cookie
33-
Result interface{}
34-
Error interface{}
35-
RawRequest *http.Request
36-
StartTime time.Time
37-
RetryAttempt int
38-
RawURL string // read only
39-
Method string
40-
Body []byte
41-
GetBody GetContentFunc
28+
PathParams map[string]string
29+
QueryParams urlpkg.Values
30+
FormData urlpkg.Values
31+
OrderedFormData []string
32+
Headers http.Header
33+
Cookies []*http.Cookie
34+
Result interface{}
35+
Error interface{}
36+
RawRequest *http.Request
37+
StartTime time.Time
38+
RetryAttempt int
39+
RawURL string // read only
40+
Method string
41+
Body []byte
42+
GetBody GetContentFunc
4243
// URL is an auto-generated field, and is nil in request middleware (OnBeforeRequest),
4344
// consider using RawURL if you want, it's not nil in client middleware (WrapRoundTripFunc)
4445
URL *urlpkg.URL
@@ -187,6 +188,12 @@ func (r *Request) SetFormData(data map[string]string) *Request {
187188
return r
188189
}
189190

191+
// SetOrderedFormData set the ordered form data from key-values pairs.
192+
func (r *Request) SetOrderedFormData(kvs ...string) *Request {
193+
r.OrderedFormData = append(r.OrderedFormData, kvs...)
194+
return r
195+
}
196+
190197
// SetFormDataAnyType set the form data from a map, which value could be any type,
191198
// will convert to string automatically.
192199
// It will not been used if request method does not allow payload.

0 commit comments

Comments
 (0)
Please sign in to comment.