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