You just need to add the Tesla.Middleware.FormUrlencoded
plug to your pipeline, then specify a map for the body of the request:
request_body = %{
"user[email]" => "link@hotmail.com",
"user[cellphone]" => 12345,
"user[country_code]" => 49
}
So, something like this:
defmodule MyClient do
use Tesla
plug Tesla.Middleware.BaseUrl, "https://api.mysite.com"
plug Tesla.Middleware.FormUrlencoded
def go do
request_body = %{
"user[email]" => "link@hotmail.com",
"user[cellphone]" => 12345,
"user[country_code]" => 49
}
path = "/protected/json/new"
{:ok, response} = post(path, request_body)
end
end
On the server side, I see the post body:
user%5Bcellphone%5D=12345&user%5Bcountry_code%5D=49&user%5Bemail%5D=link%40hotmail.com
That is different than the post body that your curl command produces:
user[email]=link@hotmail.com&user[cellphone]=12345&user[country_code]=49
However, the post body produced by curl does not conform to the spec for the Content-Type application/x-www-form-urlencoded
, which is what the -d
flag automatically sets as the Content-Type header in the curl post request:
-d, --data <data>
(HTTP) Sends the specified data in a POST request to the
HTTP server, in the same way that a browser does when a
user has filled in an HTML form and presses the submit
button. This will cause curl to pass the data to the
server using the content-type application/x-www-form-urlencoded.
The spec (see 17.13.4 Form content types) requires that all non letter/number characters, i.e. all non alphanumeric characters, be escaped with % character codes
:
application/x-www-form-urlencoded
…Forms submitted with this content type must be encoded as follows:
- …names and values are escaped. Space characters are replaced by
+
, and then reserved characters are escaped as described in RFC1738, section 2.2: Non-alphanumeric characters are replaced by %HH
, a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as “CR LF” pairs (i.e., %0D%0A
).
- The control names/values are listed in the order they appear in the document. The name is separated from the value by
=
and name/value pairs are separated from each other by &
.
curl does allow you to specify that you want the post data to be percent encoded using the --data-urlencode
flag instead of -d
, but that still doesn’t produce the same post body as tesla. This is what I get:
user[email]=link%40hotmail.com%26user%5Bcellphone%5D%3D12345%26user%5Bcountry_code%5D%3D49
For some reason, curl does not escape the brackets everywhere as can be seen in user[email]
. Confounding. Edit: Ah. To get curl to percent encode the leading name in the data, you have to prepend the data with an equals sign:
--data-urlencode '=user[email]=link@hotmail.com&user[cellphone]=12345&user[country_code]=49'
and the server receives the post body:
user%5Bemail%5D%3Dlink%40hotmail.com%26user%5Bcellphone%5D%3D12345%26user%5Bcountry_code%5D%3D49
Unfortunately, that still does not conform to the spec: curl replaces the equal signs and the ampersands with percent escapes, which confuses the server and the server can’t extract the key/value pairs. Compare to tesla:
user%5Bcellphone%5D=12345&user%5Bcountry_code%5D=49&user%5Bemail%5D=link%40hotmail.com
Tesla get’s it right and curl doesn’t.