A Lamdera package. It is not on package.elm-lang.org: the Lamdera compiler provides it, from static.lamdera.com, so lamdera install works and elm install does not.
Send HTTP requests.
Create a POST request.
Create a custom request.
An HTTP header for configuring requests.
Create a Header.
Represents the body of a Request.
Create an empty body for your Request.
Put some string in the body of your Request.
Put some JSON value in the body of your Request. This will automatically
add the Content-Type: application/json header.
Put some Bytes in the body of your Request. This allows you to use
elm/bytes to have full control over the binary
representation of the data you are sending. For example, you could create an
archive.zip file and send it along like this:
import Bytes exposing (Bytes)
zipBody : Bytes -> Body
zipBody bytes =
bytesBody "application/zip" bytesThe first argument is a MIME type
of the body. In other scenarios you may want to use MIME types like image/png
or image/jpeg instead.
Note: Use track to track upload progress.
When someone clicks submit on the <form>, browsers send a special HTTP
request with all the form data. Something like this:
POST /test.html HTTP/1.1
Host: example.org
Content-Type: multipart/form-data;boundary="7MA4YWxkTrZu0gW"
--7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="title"
Trip to London
--7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="album[]"; filename="parliment.jpg"
...RAW...IMAGE...BITS...
--7MA4YWxkTrZu0gW--This was the only way to send files for a long time, so many servers expect
data in this format. The multipartBody function lets you create these
requests. For example, to upload a photo album all at once, you could create
a body like this:
multipartBody
[ stringPart "title" "Trip to London"
, filePart "album[]" file1
, filePart "album[]" file2
, filePart "album[]" file3
]All of the body parts need to have a name. Names can be repeated. Adding the
[] on repeated names is a convention from PHP. It seems weird, but I see it
enough to mention it. You do not have to do it that way, especially if your
server uses some other convention!
The Content-Type: multipart/form-data header is automatically set when
creating a body this way.
Note: Use track to track upload progress.
One part of a multipartBody.
A part that contains String data.
multipartBody
[ stringPart "title" "Tom"
, filePart "photo" tomPng
]A part that contains a file. You can use
elm/file to get files loaded into the
browser. From there, you can send it along to a server like this:
multipartBody
[ stringPart "product" "Ikea Bekant"
, stringPart "description" "Great desk for home office."
, filePart "image[]" file1
, filePart "image[]" file2
, filePart "image[]" file3
]A part that contains bytes, allowing you to use
elm/bytes to encode data exactly in the format
you need.
multipartBody
[ stringPart "title" "Tom"
, bytesPart "photo" "image/png" bytes
]Note: You must provide a MIME type so that the receiver has clues about how to interpret the bytes.
Logic for interpreting a response body.
Expect the response body to be a String.
Expect the response body to be JSON.
Expect the response body to be binary data. For example, maybe you are talking to an endpoint that gives back ProtoBuf data:
import Bytes.Decode as Bytes
import Http
type Msg
= GotData (Result Http.Error Data)
getData : Cmd Msg
getData =
Http.get
{ url = "/data"
, expect = Http.expectBytes GotData dataDecoder
}
-- dataDecoder : Bytes.Decoder DataYou would use elm/bytes to decode the binary
data according to a proto definition file like example.proto.
If the decoder fails, you get a BadBody error that just indicates that
something went wrong. It probably makes sense to debug by peeking at the
bytes you are getting in the browser developer tools or something.
Expect the response body to be whatever.
A Request can fail in a couple ways:
BadUrl means you did not provide a valid URL.Timeout means it took too long to get a response.NetworkError means the user turned off their wifi, went in a cave, etc.BadStatus means you got a response back, but the status code indicates failure.BadBody means you got a response back with a nice status code, but the body
of the response was something unexpected. The String in this case is a
debugging message that explains what went wrong with your JSON decoder or
whatever.Note: You can use expectStringResponse and
expectBytesResponse to get more flexibility on this.
Track the progress of a request. Create a request where
tracker = Just "form.pdf" and you can track it with a subscription like
track "form.pdf" GotProgress.
There are two phases to HTTP requests. First you send a bunch of data,
then you receive a bunch of data. For example, say you use fileBody to
upload a file of 262144 bytes. From there, progress will go like this:
Sending { sent = 0, size = 262144 } -- 0.0
Sending { sent = 65536, size = 262144 } -- 0.25
Sending { sent = 131072, size = 262144 } -- 0.5
Sending { sent = 196608, size = 262144 } -- 0.75
Sending { sent = 262144, size = 262144 } -- 1.0
Receiving { received = 0, size = Just 16 } -- 0.0
Receiving { received = 16, size = Just 16 } -- 1.0With file uploads, the send phase is expensive. That is what we saw in our example. But with file downloads, the receive phase is expensive.
Use fractionSent and fractionReceived
to turn this progress information into specific fractions!
Note: The size of the response is based on the Content-Length
header, and in rare and annoying cases, a server may not include that header.
That is why the size is a Maybe Int in Receiving.
Turn Sending progress into a useful fraction.
fractionSent { sent = 0, size = 1024 } == 0.0
fractionSent { sent = 256, size = 1024 } == 0.25
fractionSent { sent = 512, size = 1024 } == 0.5
-- fractionSent { sent = 0, size = 0 } == 1.0The result is always between 0.0 and 1.0, ensuring that any progress bar
animations never go out of bounds.
And notice that size can be zero. That means you are sending a request with
an empty body. Very common! When size is zero, the result is always 1.0.
Note: If you create your own function to compute this fraction, watch out for divide-by-zero errors!
Turn Receiving progress into a useful fraction for progress bars.
fractionReceived { received = 0, size = Just 1024 } == 0.0
fractionReceived { received = 256, size = Just 1024 } == 0.25
fractionReceived { received = 512, size = Just 1024 } == 0.5
-- fractionReceived { received = 0, size = Nothing } == 0.0
-- fractionReceived { received = 256, size = Nothing } == 0.0
-- fractionReceived { received = 512, size = Nothing } == 0.0The size here is based on the Content-Length header which may be
missing in some cases. A server may be misconfigured or it may be streaming
data and not actually know the final size. Whatever the case, this function
will always give 0.0 when the final size is unknown.
Furthermore, the Content-Length header may be incorrect! The implementation
clamps the fraction between 0.0 and 1.0, so you will just get 1.0 if
you ever receive more bytes than promised.
Note: If you are streaming something, you can write a custom version of
this function that just tracks bytes received. Maybe you show that 22kb or 83kb
have been downloaded, without a specific fraction. If you do this, be wary of
divide-by-zero errors because size can always be zero!
Try to cancel an ongoing request based on a tracker.
Create a request with a risky security policy. Things like:
This is called withCredentials in JavaScript, and it allows a couple
other risky things as well. It can be useful if www.example.com needs to
talk to uploads.example.com, but it should be used very carefully!
For example, every HTTP request includes a Host header revealing the domain,
so any request to facebook.com reveals the website that sent it. From there,
cookies can be used to correlate browsing habits with specific users. “Oh, it
looks like they visited example.com. Maybe they want ads about examples!”
This is why you can get shoe ads for months without saying anything about it
on any social networks. This risk exists even for people who do not have an
account. Servers can set a new cookie to uniquely identify the browser and
build a profile around that. Same kind of tricks for logged out users.
Context: A significantly worse version of this can happen when trying to
add integrations with Google, Facebook, Pinterest, Twitter, etc. “Add our share
button. It is super easy. Just add this <script> tag!” But the goal here is
to get arbitrary access to the executing context. Now they can track clicks,
read page content, use time zones to approximate location, etc. As of this
writing, suggesting that developers just embed <script> tags is the default
for Google Analytics, Facebook Like Buttons, Twitter Follow Buttons, Pinterest
Save Buttons, and Instagram Embeds.
Expect a Response with a String body.
Expect a Response with a Bytes body.
It works just like expectStringResponse, giving you
more access to headers and more leeway in defining your own errors.
A Response can come back a couple different ways:
BadUrl_ means you did not provide a valid URL.Timeout_ means it took too long to get a response.NetworkError_ means the user turned off their wifi, went in a cave, etc.BadResponse_ means you got a response back, but the status code indicates failure.GoodResponse_ means you got a response back with a nice status code!The type of the body depends on whether you use
expectStringResponse
or expectBytesResponse.
Extra information about the response:
url of the server that actually responded (so you can detect redirects)statusCode like 200 or 404statusText describing what the statusCode means a littleheaders like Content-Length and ExpiresNote: It is possible for a response to have the same header multiple times.
In that case, all the values end up in a single entry in the headers
dictionary. The values are separated by commas, following the rules outlined
here.
Just like request, but it creates a Task.
Describes how to resolve an HTTP task.
Turn a response with a String body into a result.
Turn a response with a Bytes body into a result.
Similar to expectBytesResponse.
Just like riskyRequest, but it creates a Task. Use
with caution! This has all the same security concerns as riskyRequest.
Create a
GETrequest.