Q:

Rescue ActionClauseError

I am implementing an API endpoint. In my controller action, I would like to validate param types using guards, like this:
def get_object(conn, %{"objectId" => object_id}) when is_binary(object_id) do

When a client call this endpoint with an "objectId" not being a binary, or without "objectId" at all, phoenix raises Phoenix.ActionClauseError. I would like to rescue such exceptions, and return a custom response body (ex. {"message": "Invalid request body"}). How can I achieve this? I tried using Plug.ErrorHandler but it is reraising the error (as written in docs).
Edit: I have found a similar issue (https://github.com/phoenixframework/phoenix/issues/2386) but I would not like to create def get_object(conn, _params), do: :params_mismatch for each controller action, as there will be many of them

A:

What you found with regards to implementing a default clause for each action is the best approach, regardless of the fact that it will be present for all actions - it is explicit, and easy to reason about, and as you noted, is basically a single line of code for each action.
You could use a macro to remove the boilerplate, which might look something like:
defaction get_object(conn, %{"objectId" => object_id}) do
  ...
end

The implementation of a macro like this isn't something I'd recommend, but for fun and games, a simple implementation might look like so:
defmacro defaction(signature, do: body) do
  {name, argc} =
    case signature do
      {:when, _, [{name, _, args} | _]} ->
        {name, length(args)}
      {name, _, args} ->
        {name, length(args)}
    end
  argv = Enum.map(1..argc, fn _ -> quote do _ end end)
  quote location: :keep do
    def unquote(signature) do
      unquote(body)
    end
    def unquote(name)(unquote_splicing(argv)) do
      :params_mismatch
    end
  end
end

You could then define actions like so:
defaction get_object(conn, %{"objectId" => object_id}) do
  ...
end

or:
defaction get_object(conn, %{"objectId" => object_id}) when is_binary(object_id) do
  ...
end

I want to caution you against this approach, since it obscures the error handling, which can be seen by looking at the macro definition, but can also make local reasoning about the code more difficult.

