Passing my own :encode option to Tesla.Middleware.FormUrlencoded

Context:

I am using Tesla + Plug to ‘post’ some data to an image processing API.

This is what I am sending green=yes&blue=no&shades[]=300&shades[]=199

Notice how I have the same parameter shades with two values 300 and 199

Unfortunately the image processing API returns 'shades' is missing

The parameter is shades not shades[] so I should be sending green=yes&blue=no&shades=300&shades=199 without the square brackets.

Spotting The Problem:

plug Tesla.Middleware.FormUrlencoded,
    encode: &Plug.Conn.Query.encode/1,
    decode: &Plug.Conn.Query.decode/1

The issue is coming from plug Here

# covers non-keyword lists
  defp encode_pair(parent_field, list, encoder) when is_list(list) do
    mapper = fn
      value when is_map(value) and map_size(value) != 1 ->
        raise ArgumentError,
              "cannot encode maps inside lists when the map has 0 or more than 1 element, " <>
                "got: #{inspect(value)}"

      value ->
        # The Issue Is Here, You Can See The Square Brackets 
        [?&, encode_pair(parent_field <> "[]", value, encoder)]
    end

My Questions:

  1. How do I pass my own :encode option to Tesla.Middleware.FormUrlencoded

  2. Why the plug repository maintainers do not have an option for this?

Just create a module that contains your encode function and use it in the plug:

plug Tesla.Middleware.FormUrlencoded,
    encode: &MyEncoder.encode/1,
    decode: &Plug.Conn.Query.decode/1

You’re basically just telling the Tesla.Middleware.FormUrlencoded plug what function to call to do the encoding. Just make sure it takes 1 argument and returns what it expects, which in this case I believe is a String.

That I’m not exactly sure, but if I had to guess, it’s probably because they are following what would be considered to be best practice.


Since the Tesla.Middleware.FormUrlencoded allows you to set the encoder, your encoder could be something as simple as:

defmodule MyEncoder do
  def encode(map_or_list_of_tuples) do
    map_or_list_of_tuples
    |> Plug.Conn.Query.encode()
    |> String.replace("[]", "")
  end
end
1 Like