Joe McBroom
Joe McBroom

Reputation: 53

Pattern matching HTTPoison.Response

I'm trying handle the response from an API call using HTTPoison

In my app context I have

def add_to_cart(data, user) do
  case HTTPoison.post!(
      "https://api.moltin.com/v2/carts/" <> user <> 
      "/items", data,
      [{"Authorization",
        "Bearer #{Agent.get(:token, fn state -> state end)}"},
       {"Content-Type", "application/json"}] do

    {:ok, %HTTPoison.Response{status_code: 201, body: body}} ->
      case Poison.decode(body) do
        {:ok, decoded} -> decoded
        {:error, error} -> {:error, error}
      end

    {:ok, %HTTPoison.Response{status_code: 404}} ->
      IO.puts "Not found :("

    {:error, %HTTPoison.Error{reason: reason}} ->
      IO.inspect reason

  end
end

The error I return is:

CaseClauseError at POST /order
no case clause matching: %HTTPoison.Response{response content}

I have used this pattern elsewhere in my code and it matches fine but when I place it here, it doesn't seem to work.

I'm sure this isn't enough information with which to work but I'm not sure at this point what else to put. Any guidance would be appreciated.

Upvotes: 4

Views: 2545

Answers (3)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

The Elixir convention is: banged versions of functions—instead of {:ok, result} and {:error, reason}—return result on success and raise on error. Grep this page for “Elixir doesn’t impose exceptions” for the reference.

HTTPoison follows this convention.

To make your code work, one should either use HTTPoison.post (not banged) in the very top case, or match the result respectively:

try
  %HTTPoison.Response{status: status, body: body} =
    HTTPoison.post!(...)
  case status do
    201 -> ...
    404 -> ...
  end
rescue
  e in HTTPoison.Error ->
    IO.inspect e
end  

Upvotes: 9

Kociamber
Kociamber

Reputation: 1155

What about a nice and classy pattern matching suggested by HTTPoison docs? Just get rid of exclamation mark and try something like this:

case HTTPoison.post(stuff) do
  {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
    decoded_stuff = Poison.decode(body) # use another nice case statement here
  {:ok, %HTTPoison.Response{status_code: 404}} ->
    IO.puts "Not found :("
  {:error, %HTTPoison.Error{reason: reason}} ->
    IO.inspect reason
end

Upvotes: 3

Tyler
Tyler

Reputation: 18177

According to that error message you don't need the :ok and :error atoms in your tuples, try changing them from:

{:ok, %HTTPoison.Response{status_code: 201, body: body}} -> #...
{:error, %HTTPoison.Error{reason: reason}} -> #...

To:

%HTTPoison.Response{status_code: 201, body: body} -> #...
%HTTPoison.Error{reason: reason} -> #...

I think in other places in your app you are probably using post/4 instead of post!/4, post/4 (without the !) returns the tuple with the :ok atom, while post post!/4 (with the !) kind of assumes that everything will work, doesn't return an atom in the tuple, and will throw an exception if something goes wrong.

Upvotes: 1

Related Questions