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
57 changes: 54 additions & 3 deletions docs/content-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ If you specify a content type of `'application/json'`, `HttpCommand` will automa
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'`:

* `Params` must be a namespace with named elements.
* Each element in `Params` consists of the data for the element optionally followed by a content type for the element.
* Each element in `Params` consists of up to 3 elements:
* content - the data for the element, if sending a file this is the file name (see the section below)
* type - the MIME content-type type for the element
* filename - if content is to be saved as a file, this is the filename for the content
* To send a file, prefix the file name with either:
* `@` to upload the file's content and its name
* `<` to upload just the file's content
Expand All @@ -22,17 +25,65 @@ In the example below:
* Extra newlines have been removed for compactness.
* The file `/tmp/foo.txt` contains `Hello World`.
* We create 4 parts to be sent with the request:
* a simple string
* a JSON array (with content type 'application/json')
* a named file - both the content and file name will be sent
* some in-workspace content to be saved as a file named 'data.txt'
* a simple string
* an unnamed file - only the content will be sent
* a JSON array (with content type 'application/json')

```
h←HttpCommand.New 'post' 'someurl.com'
p←⎕NS '' ⍝ create a namespace
p.json←'[1,2,3]' 'application/json' ⍝ value and content type
p.namedfile←'@/tmp/foo.txt' ⍝ @ = include the file name
p.saveasfile←'this is the content' 'text/plain' 'data.txt' ⍝ save content as a file
p.string←'/tmp/foo.txt' ⍝ just a value
p.unnamedfile←'</tmp/foo.txt' ⍝ < = do not include the file name
h.Params←p ⍝ assign the request Params
h.ContentType←'multipart/form-data' ⍝
h.Show
POST / HTTP/1.1
Host: someurl.com
User-Agent: Dyalog-HttpCommand/5.9.1
Accept: */*
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
Content-Length: 806

--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
Content-Disposition: form-data; name="json"
Content-Type: application/json
[1,2,3]

--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
Content-Disposition: form-data; name="namedfile"; filename="foo.txt"
Content-Type: text/plain
this is a test

--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
Content-Disposition: form-data; name="saveasfile"; filename="data.txt"
Content-Type: text/plain
this is the content

--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
Content-Disposition: form-data; name="string"
/tmp/foo.txt

--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q
Content-Disposition: form-data; name="unnamedfile"
Content-Type: text/plain
this is a test
--YqXdULkJhPcuCBBikV8ffM8zS7uAdFoME4jNpDDs7SxVw9mM4q--
```

```
h←HttpCommand.New 'post' 'someurl.com'
p←⎕NS '' ⍝ create a namespace
p.string←'/tmp/foo.txt' ⍝ just a value
p.namedfile←'@/tmp/foo.txt' ⍝ @ = include the file name
p.unnamedfile←'</tmp/foo.txt' ⍝ < = do not include the file name
p.json←'[1,2,3]' 'application/json' ⍝ value and content type
p.saveasfile←'this is the content' 'text/plain' 'data.txt'
h.Params←p ⍝ assign the request Params
h.ContentType←'multipart/form-data' ⍝
h.Show
Expand Down
15 changes: 7 additions & 8 deletions source/HttpCommand.dyalog
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
∇ r←Version
⍝ Return the current version
:Access public shared
r←'HttpCommand' '5.9.0' '2025-02-24'
r←'HttpCommand' '5.9.1' '2025-03-08'

⍝ Request-related fields
Expand Down Expand Up @@ -192,8 +192,8 @@
⍝ Shared method to perform an HTTP request with JSON data as the request and response payloads
⍝ args - [URL] | [Command URL Params Headers Cert SSLFlags Priority]
:Access public shared
:If 0=⎕NC'requestOnly' ⋄ requestOnly←¯1 ⋄ :EndIf

:If 0=⎕NC'requestOnly' ⋄ requestOnly←¯1 ⋄ :EndIf
:If isSimpleChar args ⍝ simple character vector args?
:AndIf (args≡'localhost')≥∧/args∊over lc ⎕A ⋄ args←'GET'args ⋄ :EndIf ⍝ localhost or only alphabetics?

Expand Down Expand Up @@ -998,18 +998,15 @@
:EndTrap

∇ (payload msg)←boundary multipart parms;name;value;filename;contentType;content
∇ (payload msg)←boundary multipart parms;name;value;filename;contentType;content;fileName
⍝ format multipart/form-data payload
⍝ parms is a namespace with named objects
msg←payload←''
:For name :In parms.⎕NL ¯2
payload,←'--',boundary
(value contentType)←2↑(⊆parms⍎name),''
(value contentType fileName)←3↑(⊆parms⍎name),'' ''
payload,←NL,'Content-Disposition: form-data; name="',name,'"'
:If ~0∊⍴contentType
payload,←NL,'Content-Type: ',contentType
:EndIf
:If '@<'∊⍨⊃value
:If ⎕NEXISTS 1↓value
:AndIf 2=1 ⎕NINFO 1↓value
Expand All @@ -1021,6 +1018,8 @@
→0⊣msg←'File not found: "',(1↓value),'"'
:EndIf
:Else
payload,←(~0∊⍴fileName)/'; filename="',(∊¯2↑1 ⎕NPARTS fileName),'"'
payload,←(~0∊⍴contentType)/NL,'Content-Type: ',contentType
payload,←NL,NL,(∊⍕value),NL
:EndIf
:EndFor
Expand Down