3
votes

I have multiple controller methods, where I am pattern matching with the coming parameters one of them is

  def index(conn, %{
        "id" => camera_exid,
        "from" => from,
        "to" => to,
        "limit" => "3600",
        "page" => _page
      }) do

when user requests such as http://localhost:4000/v2/cameras/pocri-yweod/recordings/snapshots?limit=3600&page=1

it throws an error, and of course, it supposes to through error, but is there any way to handle such error more gracefully than an exception? without creating another index with fewer values to pattern match?

I tried creating a fallback controller very basic

defmodule EvercamMediaWeb.FallbackController do
  use EvercamMediaWeb, :controller

  def call(conn, what) do
    IO.inspect(what)
    render_error(conn, 400, "error.")
  end
end

but it didn't work.

Is it possible to make it for the whole controller? when the pattern matched parameters doesn't match, it returns 400 while saying which params are missing? I am the only pattern matching those parameters in the head which are definite.

1
in your case matching in definition of controller directly is incorrect, just match it as a simple parameter and inside of function get the data you need from it.Daniel
@Daniel “Incorrect” word is plain wrong. It’s perfectly fine to match whatever in function clauses. One just needs sink-all clause def index(conn, incomplete), do: render_error ....Aleksei Matiushkin
it makes no sense since his parameters are optional, in this case you can't match all the possible combinations so you just don't match and try to extract from the parameter what keys you need.Daniel
if there are 20 def's then creating 20 def of incomplete? is not really a good solution, is there anything we can do such as a plug? or any solution which can be global? @AlekseiMatiushkinJunaid Farooq

1 Answers

2
votes

Yes, it's possible to make it for the whole controller, as phoenix injects an action/2 plug in controllers which -by default- calls the function matched from the router, and it can be overridden.

try this in your controller:

  def action(conn, _) do
    required_fields = %{
      index: ["id", "from", "to", "limit", "page"],
      action2: ["id", "x", "y"]
    }

    args = [conn, conn.params]

    missing_fields =
      Enum.reject(required_fields[action_name(conn)], fn x -> x in Map.keys(conn.params) end)

    case missing_fields do
      [] ->
        apply(__MODULE__, action_name(conn), args)

      fields ->
        conn
        |> put_status(:bad_request)
        |> text("missing fields: " <> inspect(fields))
    end
  end

you can render the way you like, in case missing_fields list is not empty.

ps: returning error 400 is not an exception, it's a well handled error, as stated by Jose here https://github.com/phoenixframework/phoenix/issues/2386#issuecomment-315590839