DEV Community

TamakiMaeda
TamakiMaeda

Posted on • Edited on

Phoenix.Router.NoRouteError 発生時のレスポンス

Phoenix 1.3 では action_fallback が追加されたためエラーハンドリングがとても簡単になりました。
http://phoenixframework.org/blog/phoenix-1-3-0-released

しかし、Phoenix.Router.NoRouteError が発生した場合にはコントローラーまで処理が届かないため、 action_fallback は使用できません。
そこで、 Plug.ErrorHandler を使用して NoRouteError に対応してみようと思います。

まず、以下のような ErrorView を作成します。

# views/error_view.ex
defmodule HelloWeb.ErrorView do
  use HelloWeb, :view

  def render("404.json", _assigns) do
    %{errors: %{detail: "Page not found"}}
  end

  def render("500.json", _assigns) do
    %{errors: %{detail: "Internal server error"}}
  end

  def render(_template, assigns) do
    render "500.json", assigns
  end
end
Enter fullscreen mode Exit fullscreen mode

次に、 Plug.ErrorHandler.handle_errors をオーバーライドするモジュールを定義します。

# router_error_handling.ex
defmodule HelloWeb.RouterErrorHandling do
  defmacro __using__(_) do
    quote do
      use Plug.ErrorHandler

      def handle_errors(conn, %{reason: reason} = %{reason: %Phoenix.Router.NoRouteError{}}) do
        send_resp(conn, conn.status, Poison.encode!(HelloWeb.ErrorView.render("#{reason.plug_status}.json", %{})))
      end
      def handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
        send_resp(conn, conn.status, Poison.encode!(HelloWeb.ErrorView.render("500.json", %{})))
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

最後に上記モジュールを Router 内で use して終わりです。

# router.ex
defmodule HelloWeb.Router do
  use HelloWeb, :router
  use HelloWeb.RouterErrorHandling

 ...
end
Enter fullscreen mode Exit fullscreen mode

独自実装の use 反対派の宗教に入っている方は RouterErrorHandling を定義せずに Router 内で直接 Plug.ErrorHandler を use して handle_errors を書く感じでも動作はしました。
私の場合は、 Router にルーティング定義以外の処理を書いてルーティングの見通しが悪くなるのが嫌だったので別モジュールにしています。

Poison.encode!(HelloWeb.ErrorView.render("#{reason.plug_status}.json", %{})) に気持ち悪さを感じるので、この辺いい感じにしたい......。

Top comments (0)