Skip to content

Commit beff353

Browse files
authored
59 improve content type multipart/form-data handling when sending files (#60)
* Address #59 - improved multipart/forn-data handling * Update content-types.md
1 parent c4014cb commit beff353

2 files changed

Lines changed: 61 additions & 11 deletions

File tree

docs/content-types.md

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ If you specify a content type of `'application/json'`, `HttpCommand` will automa
1111
Content type `multipart/form-data` is commonly used to transfer files to a server or to send multiple types of content in the request payload. If you specify a content type of `'multipart/form-data'`:
1212

1313
* `Params` must be a namespace with named elements.
14-
* Each element in `Params` consists of the data for the element optionally followed by a content type for the element.
14+
* Each element in `Params` consists of up to 3 elements:
15+
* content - the data for the element, if sending a file this is the file name (see the section below)
16+
* type - the MIME content-type type for the element
17+
* filename - if content is to be saved as a file, this is the filename for the content
1518
* To send a file, prefix the file name with either:
1619
* `@` to upload the file's content and its name
1720
* `<` to upload just the file's content
@@ -22,17 +25,65 @@ In the example below:
2225
* Extra newlines have been removed for compactness.
2326
* The file `/tmp/foo.txt` contains `Hello World`.
2427
* We create 4 parts to be sent with the request:
25-
* a simple string
28+
* a JSON array (with content type 'application/json')
2629
* a named file - both the content and file name will be sent
30+
* some in-workspace content to be saved as a file named 'data.txt'
31+
* a simple string
2732
* an unnamed file - only the content will be sent
28-
* a JSON array (with content type 'application/json')
33+
34+
```
35+
h←HttpCommand.New 'post' 'someurl.com'
36+
p←⎕NS '' ⍝ create a namespace
37+
p.json←'[1,2,3]' 'application/json' ⍝ value and content type
38+
p.namedfile←'@/tmp/foo.txt' ⍝ @ = include the file name
39+
p.saveasfile←'this is the content' 'text/plain' 'data.txt' ⍝ save content as a file
40+
p.string←'/tmp/foo.txt' ⍝ just a value
41+
p.unnamedfile←'</tmp/foo.txt' ⍝ < = do not include the file name
42+
h.Params←p ⍝ assign the request Params
43+
h.ContentType←'multipart/form-data' ⍝
44+
h.Show
45+
POST / HTTP/1.1
46+
Host: someurl.com
47+
User-Agent: Dyalog-HttpCommand/5.9.1
48+
Accept: */*
49+
Accept-Encoding: gzip, deflate
50+
Content-Type: multipart/form-data; boundary=YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
51+
Content-Length: 806
52+
53+
--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
54+
Content-Disposition: form-data; name="json"
55+
Content-Type: application/json
56+
[1,2,3]
57+
58+
--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
59+
Content-Disposition: form-data; name="namedfile"; filename="foo.txt"
60+
Content-Type: text/plain
61+
this is a test
62+
63+
--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
64+
Content-Disposition: form-data; name="saveasfile"; filename="data.txt"
65+
Content-Type: text/plain
66+
this is the content
67+
68+
--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
69+
Content-Disposition: form-data; name="string"
70+
/tmp/foo.txt
71+
72+
--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
73+
Content-Disposition: form-data; name="unnamedfile"
74+
Content-Type: text/plain
75+
this is a test
76+
--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q--
77+
```
78+
2979
```
3080
h←HttpCommand.New 'post' 'someurl.com'
3181
p←⎕NS '' ⍝ create a namespace
3282
p.string←'/tmp/foo.txt' ⍝ just a value
3383
p.namedfile←'@/tmp/foo.txt' ⍝ @ = include the file name
3484
p.unnamedfile←'</tmp/foo.txt' ⍝ < = do not include the file name
3585
p.json←'[1,2,3]' 'application/json' ⍝ value and content type
86+
p.saveasfile←'this is the content' 'text/plain' 'data.txt'
3687
h.Params←p ⍝ assign the request Params
3788
h.ContentType←'multipart/form-data' ⍝
3889
h.Show

source/HttpCommand.dyalog

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
rVersion
88
Return the current version
99
:Access public shared
10-
r'HttpCommand' '5.9.0' '2025-02-24'
10+
r'HttpCommand' '5.9.1' '2025-03-08'
1111
1212

1313
Request-related fields
@@ -192,8 +192,8 @@
192192
Shared method to perform an HTTP request with JSON data as the request and response payloads
193193
args - [URL] | [Command URL Params Headers Cert SSLFlags Priority]
194194
:Access public shared
195-
:If 0=⎕NC'requestOnly' requestOnly¯1 :EndIf
196-
195+
:If 0=⎕NC'requestOnly' requestOnly¯1 :EndIf
196+
197197
:If isSimpleChar args simple character vector args?
198198
:AndIf (args'localhost')/argsover lc ⎕A args'GET'args :EndIf localhost or only alphabetics?
199199

@@ -998,18 +998,15 @@
998998
:EndTrap
999999
10001000

1001-
(payload msg)boundary multipart parms;name;value;filename;contentType;content
1001+
(payload msg)boundary multipart parms;name;value;filename;contentType;content;fileName
10021002
format multipart/form-data payload
10031003
parms is a namespace with named objects
10041004
10051005
msgpayload''
10061006
:For name :In parms.⎕NL ¯2
10071007
payload,'--',boundary
1008-
(value contentType)2(parmsname),''
1008+
(value contentType fileName)3(parmsname),'' ''
10091009
payload,NL,'Content-Disposition: form-data; name="',name,'"'
1010-
:If ~0contentType
1011-
payload,NL,'Content-Type: ',contentType
1012-
:EndIf
10131010
:If '@<'value
10141011
:If ⎕NEXISTS 1value
10151012
:AndIf 2=1 ⎕NINFO 1value
@@ -1021,6 +1018,8 @@
10211018
0msg'File not found: "',(1value),'"'
10221019
:EndIf
10231020
:Else
1021+
payload,(~0fileName)/'; filename="',(¯21 ⎕NPARTS fileName),'"'
1022+
payload,(~0contentType)/NL,'Content-Type: ',contentType
10241023
payload,NL,NL,(value),NL
10251024
:EndIf
10261025
:EndFor

0 commit comments

Comments
 (0)