elixirphoenix-frameworkelixir-poisonhttpoison

Getting argument error when fetching data from API and decoding with Poison


I am new to Elixir/Phoenix and trying to learn by building a small app.

I am fetching data from a 3rd party API and keep getting the following error.

(ArgumentError) argument error :erlang.iolist_to_binary([%{"24h_volume" => "1000", "name" => "some_name"},{...}])

What I have in my controller is:

HTTPoison.start

%HTTPoison.Response{body: body} = HTTPoison.get!(url)
body = body
       |> Poison.decode!(keys: :atoms!)

This does not work. I have used (keys: :atoms) which is discouraged in the Poison documentation.

Here is my schema:

schema "things" do
  field :name, :string
  field :volume_24h, :float

  timestamps()
end

@doc false
def changeset(%Thing{} = thing, attrs) do
  thing
  |> cast(attrs, [:volume_24h, :name])
  |> validate_not_nil([:volume_24h, :name])
end

def validate_not_nil(changeset, fields) do
  Enum.reduce(fields, changeset, fn field, changeset ->
    if get_field(changeset, field) == nil do
      add_error(changeset, field, "nil")
    else
      changeset
    end
  end)
end

I am trying to use a different field name for "24h_volume" and I get this error:

(ArgumentError) argument error :erlang.binary_to_existing_atom("24h_volume", :utf8)

I am clearly missing something here.

Is there a way to pass the desired field name to Poison because "24h_volume" will not be a valid atom? How can I fix these errors?


Solution

  • You have a mess with volume_24h parameter.

    As it is stated in Poison documentation:

    Note that keys: :atoms! reuses existing atoms, i.e. if :name was not allocated before the call, you will encounter an argument error message.

    This is exactly what happens. The application expects the :volume_24h key to come from the request, but it (for some reason, possibly due to a form misconfiguration of like) receives 24h_volume. By using the permissive atoms call instead of atoms! you did not solve anything, you have the issue hidden. What actually happens, 24h_volume key comes and is being effectively discarded by the call to cast.

    What you need is either to fix the fronend/request sender to send volume_24h key, or to fix the controller to accept :"24h_volume" key.


    There are two reasons behind discouraging atoms usage. The one is described in the Poison documentation: so called “atoms DOS attack” is possible with the random requests having random keys being issued subsequently, overflowing the atom storage. The second is that by using banged version atoms! one protects themselves from typos/misconfiguration like the one above.

    FWIW, the atom for the proper key is being allocated in schema definition.