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
8 changes: 7 additions & 1 deletion lib/tesla/middleware/form_urlencoded.ex
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ defmodule Tesla.Middleware.FormUrlencoded do
field on Stripe-style update endpoints.
- Map keys are not ordered; keyword lists and lists of 2-tuples
preserve the order you give them.
- A body that is already a binary is left untouched and no
`content-type` header is added, matching `Tesla.Middleware.JSON`. This
is what lets both middlewares share one stack: `JSON` encodes the body
and claims `application/json`, and this middleware then sees a binary
and stays out of the way rather than appending a second `content-type`.
Encode the body yourself and you own its `content-type` too.
- Decoding stays flat. `Plug.Conn.Query.decode/1` will parse the
bracket keys into nested maps, but indexed lists come back as maps
keyed by string indices (`"0"`, `"1"`, …), not as Elixir lists, so
Expand Down Expand Up @@ -221,10 +227,10 @@ defmodule Tesla.Middleware.FormUrlencoded do

defp encodable?(%{body: {:form_urlencoded, _}}), do: true
defp encodable?(%{body: nil}), do: false
defp encodable?(%{body: body}) when is_binary(body), do: false
defp encodable?(%{body: %Tesla.Multipart{}}), do: false
defp encodable?(_), do: true

defp encode_body(body, _opts) when is_binary(body), do: body
defp encode_body({:form_urlencoded, data}, opts), do: do_encode(data, opts)
defp encode_body(body, opts), do: do_encode(body, opts)

Expand Down
34 changes: 34 additions & 0 deletions test/tesla/middleware/form_urlencoded_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,45 @@ defmodule Tesla.Middleware.FormUrlencodedTest do
assert env.body == "application/x-www-form-urlencoded"
end

test "no content-type is set if body is binary" do
assert {:ok, env} = Client.post("/check_incoming_content_type", "data")
assert env.body == nil
end

test "decode response" do
assert {:ok, env} = Client.get("/decode_response")
assert env.body == %{"x" => "1", "y" => "2"}
end

defmodule JsonStackClient do
use Tesla

plug Tesla.Middleware.JSON
plug Tesla.Middleware.FormUrlencoded

adapter fn env ->
{:ok,
%{
env
| status: 200,
headers: [{"content-type", "text/html"}],
body: Tesla.get_headers(env, "content-type")
}}
end
end

describe "sharing a stack with Tesla.Middleware.JSON" do
test "a json body is left with a single application/json content-type" do
assert {:ok, env} = JsonStackClient.post("/post", %{"foo" => "bar"})
assert env.body == ["application/json"]
end

test "a tagged form body is left with a single form content-type" do
assert {:ok, env} = JsonStackClient.post("/post", {:form_urlencoded, %{"foo" => "bar"}})
assert env.body == ["application/x-www-form-urlencoded"]
end
end

defmodule MultipartClient do
use Tesla

Expand Down
Loading