The HTTP protocol allows to carry additional data either by using the request URI, or by using the HTTP body, for which there are a few already widely used standards. HTTPX
aims at solving the majority.
Using the :params
option will encode the parameters in the URI query in the x-www-form-urlencoded
format:
# GET https://google.com?q=john
HTTPX.get("http://google.com", params: { "q" => "john" })
Using the :body
option will pass the raw data to the request body. You can pass:
File
, or a StringIO
object);#each
and yields strings (such as ["a", "b", "c"]
);The content type will be set to application/octet-stream
, unless stated explicitly otherwise.
# body as a string
HTTPX.post("https://example.com", body: "thiswillbethebody")
# body as IO object
HTTPX.post("https://example.com", body: File.open("large-file.txt"))
HTTPX.post("https://example.com", body: StringIO.new("largetext"))
# As an object implementing each
HTTPX.post("https://example.com", body: %w[this will be the body])
# or
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a.to_s
a, b = b, a + b
break if a > (2 ** 32)
end
end
HTTPX.post("https://example.com", body: fib)
# Note: The `content-length` header won't be sent in this case, and chunks will be immediately sent to the server when yielded.
Using the :json
option will encode the parameters in the application/json
format:
# POSTed as '{"foo":"bar"}'
HTTPX.post("https://example.com", json: { "foo" => "bar" })
Using the :form
option will encode the parameters in the request body encoded in the x-www-form-urlencoded
format:
# POSTed as "foo=bar"
HTTPX.post("https://example.com", form: { "foo" => "bar" })
If the :form
param contains files or pathnames (or parameters with a given specification explained below), the request will be encoded as multipart/form-data
:
path = "path/to/image.jpg"
# with pathnames
response = HTTPX.post("https://example.com", form: { image: Pathname.new(path) })
# or with files
response = HTTPX.post("https://example.com", form: { image: File.new(path) })
#
# ...
# Content-Type: multipart/form-data; boundary=---------------------a324a748aade297ac4ffc87108bee9ecd8cb2c2fea ...
# "-----------------------2a6965a264bbf67e519ad96cbc970800cac79dd240
# ...
# Content-Disposition: form-data; name="image"; filename="image.jpg"
# Content-Type: image/jpeg
#
# \xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xFF\xDB\x00\x84\x00\x02\x02\x02
In multipart payloads, the content type of String values will be set as “text/plain”.
For files, the type will be inferred using one of the following strategies:
filemagic
gem (if available);marcel
gem (if available);mime-magic
gem (if available);file
cli tool (if installed in the system);application/octet-stream
as fallback;HTTPX.post("https://example.com", form: { image: Pathname.new(path) })
caveat: the integrations above are oriented towards media files which typically contain information about its first bytes, such as photos and videos, but may not work as well with text-based mime-types (such as “text/csv”).
You can override passing the file (or string) inside an hash with the following options:
HTTPX.post("https://example.com", form: {
image: {
content_type: "image/png",
filename: "image.jpg",
body: Pathname.new("/path/to/file-xxx-yyy-zzz-random-data-uuid")
},
metadata: {
content_type: "application/json",
body: JSON.dump({ location: "Japan", frame_number: 2 })
}
})
you can also pass your own object responding to those fields as methods, and implementing #read(?Integer bytesize, ?String buffer)
:
class AwesomeVideo
def content_type
"video/mp4"
end
def filename
"gangnam-style.mp4"
end
def read(*)
# .....
# .....
end
# optionally, you can implement
# def close ; end
# and
# def rewind ; end
end
HTTPX.post("https://example.com", form: {
video: AwesomeVideo.new
})
If using “HTTP/1.1”, you can set the header, and httpx
will encode it using the chunked transfer encoding immediately:
HTTPX.with(headers: { "transfer-encoding" => "chunked" })
.post("https://example.com", body: %w[this will be the body])
If you want to give the server the opportunity to validate the HTTP request headers before the body is sent, you can just add the expect header, and the client will seamlessly respect it:
HTTPX.post("https://example.com", headers: { "expect" => "100-continue" }, body: File.open("body.txt"))
However, if you aren’t sure if the server you’re requesting from does not handle it, you are strongly recommended to use the expect
plugin instead, which gracefully degrades to sending the body anyway after a timeout if the server doesn’t.
Next: Pass Headers