Spec-Zone.ru › Phoenix

Пользовательские страницы ошибок

Исходный код

В новых проектах Phoenix есть два представления ошибок с именами ErrorHTML и ErrorJSON, которые находятся в lib/hello_web/controllers/. Эти представления предназначены для обработки ошибок каждого формата в общем виде из одного централизованного места.

Представления ошибок

В новых приложениях представления ErrorHTML и ErrorJSON выглядят так:

defmodule HelloWeb.ErrorHTML do
  use HelloWeb, :html

  # If you want to customize your error pages,
  # uncomment the embed_templates/1 call below
  # and add pages to the error directory:
  #
  #   * lib/<%= @lib_web_name %>/controllers/error_html/404.html.heex
  #   * lib/<%= @lib_web_name %>/controllers/error_html/500.html.heex
  #
  # embed_templates "error_html/*"

  # The default is to render a plain text page based on
  # the template name. For example, "404.html" becomes
  # "Not Found".
  def render(template, _assigns) do
    Phoenix.Controller.status_message_from_template(template)
  end
end

defmodule HelloWeb.ErrorJSON do
  # If you want to customize a particular status code,
  # you may add your own clauses, such as:
  #
  # def render("500.json", _assigns) do
  #   %{errors: %{detail: "Internal Server Error"}}
  # end

  # By default, Phoenix returns the status message from
  # the template name. For example, "404.json" becomes
  # "Not Found".
  def render(template, _assigns) do
    %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
  end
end

Прежде чем перейти к подробностям, посмотрим, как сообщение 404 Not Found выглядит в браузере. В среде разработки Phoenix по умолчанию отлаживает ошибки и показывает очень информативную страницу отладки. Однако здесь мы хотим увидеть страницу, которую приложение отобразило бы в рабочей среде. Для этого нужно установить debug_errors: false в config/dev.exs.

import Config

config :hello, HelloWeb.Endpoint,
  ...,
  debug_errors: false,
  code_reloader: true,
  ...

После изменения файла конфигурации нужно перезапустить сервер, чтобы изменения вступили в силу. После перезапуска сервера откроем http://localhost:4000/such/a/wrong/path в запущенном локальном приложении и посмотрим, что получится.

Ну, это не очень впечатляет. Мы видим обычную строку "Not Found" без разметки и оформления.

Первый вопрос: откуда берётся эта строка ошибки? Ответ находится прямо в ErrorHTML.

def render(template, _assigns) do
  Phoenix.Controller.status_message_from_template(template)
end

Отлично, у нас есть функция render/2, которая принимает шаблон и карту assigns, которую мы игнорируем. Когда вы вызываете render(conn, :some_template) из контроллера, Phoenix сначала ищет функцию some_template/1 в модуле представления. Если такой функции нет, он вызывает render/2, передавая шаблон и имя формата, например "some_template.html".

Иными словами, чтобы создать пользовательские страницы ошибок, достаточно определить подходящую ветвь функции render/2 в HelloWeb.ErrorHTML.

  def render("404.html", _assigns) do
    "Page Not Found"
  end

Но можно сделать ещё лучше.

Phoenix генерирует для нас ErrorHTML, но не создаёт каталог lib/hello_web/controllers/error_html. Давайте создадим его. В новом каталоге добавим шаблон с именем 404.html.heex и разметку — сочетание макета приложения и нового <div> с сообщением для пользователя.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>Welcome to Phoenix!</title>
    <link rel="stylesheet" href="/assets/css/app.css"/>
    <script defer type="text/javascript" src="/assets/js/app.js"></script>
  </head>
  <body>
    <header>
      <section class="container">
        <nav>
          <ul>
            <li><a href="https://phoenix.hexdocs.pm/overview.html">Get Started</a></li>
          </ul>
        </nav>
        <a href="https://phoenixframework.org/" class="phx-logo">
          <img src="/images/logo.svg" alt="Phoenix Framework Logo"/>
        </a>
      </section>
    </header>
    <main class="container">
      <section class="phx-hero">
        <p>Sorry, the page you are looking for does not exist.</p>
      </section>
    </main>
  </body>
</html>

После создания файла шаблона не забудьте удалить соответствующую ветвь render/2 для этого шаблона, иначе функция переопределит шаблон. Сделаем это для ветви 404.html, которую мы ранее добавили в lib/hello_web/controllers/error_html.ex. Также нужно указать Phoenix, что шаблоны необходимо встроить в модуль:

+ embed_templates "error_html/*"

- def render("404.html", _assigns) do
-  "Page Not Found"
- end

Теперь, если вернуться на http://localhost:4000/such/a/wrong/path, мы увидим гораздо более приятную страницу ошибки. Стоит отметить, что мы не отображали шаблон 404.html.heex через макет приложения, хотя хотим, чтобы наша страница ошибки выглядела так же, как и остальные страницы сайта. Это нужно, чтобы избежать циклических ошибок. Например, что произойдёт, если приложение завершилось с ошибкой из-за ошибки в макете? Повторная попытка отобразить макет приведёт к новой ошибке. Поэтому в идеале нужно свести к минимуму зависимости и логику в шаблонах ошибок, используя только самое необходимое.

Пользовательские исключения

В Elixir есть макрос defexception/1 для определения пользовательских исключений. Исключения представлены структурами, а структуры нужно определять внутри модулей.

Чтобы создать пользовательское исключение, нужно определить новый модуль. По соглашению, в его названии используется слово "Error". Внутри этого модуля нужно определить новое исключение с помощью defexception/1; файл lib/hello_web.ex кажется подходящим местом для него.

defmodule HelloWeb.SomethingNotFoundError do
  defexception [:message]
end

Новое исключение можно вызвать так:

raise HelloWeb.SomethingNotFoundError, "oops"

По умолчанию Plug и Phoenix считают все исключения ошибками 500. Однако в Plug есть протокол Plug.Exception, с помощью которого можно настроить статус и добавить действия, которые структуры исключений могут возвращать на странице отладки ошибок.

Если для ошибки HelloWeb.SomethingNotFoundError нужно указать статус 404, можно определить реализацию протокола Plug.Exception в lib/hello_web.ex следующим образом:

defimpl Plug.Exception, for: HelloWeb.SomethingNotFoundError do
  def status(_exception), do: 404
  def actions(_exception), do: []
end

Также можно определить поле plug_status непосредственно в структуре исключения:

defmodule HelloWeb.SomethingNotFoundError do
  defexception [:message, plug_status: 404]
end

Однако ручная реализация протокола Plug.Exception может быть удобна в некоторых случаях, например при создании ошибок с предлагаемыми действиями.

Ошибки с предлагаемыми действиями

Действия исключений — это функции, которые можно вызвать со страницы ошибки. По сути, это список карт, задающих label и handler для выполнения. Например, Phoenix отобразит ошибку, если у вас есть ожидающие миграции, и добавит на страницу ошибки кнопку для их выполнения.

Если debug_errors равно true, они отображаются на странице ошибки в виде набора кнопок и имеют следующий формат:

[
  %{
    label: String.t(),
    handler: {module(), function :: atom(), args :: []}
  }
]

Чтобы вернуть действия для HelloWeb.SomethingNotFoundError, можно реализовать Plug.Exception следующим образом:

defimpl Plug.Exception, for: HelloWeb.SomethingNotFoundError do
  def status(_exception), do: 404

  def actions(_exception) do
    [
      %{
        label: "Run seeds",
        handler: {Code, :eval_file, ["priv/repo/seeds.exs"]}
      }
    ]
  end
end
← Предыдущая страница Шпаргалка по маршрутизации
Следующая страница → Загрузка файлов

Пакет Hex Предпросмотр Hex (текущий файл) Скачать версию ePub

Создано с помощью ExDoc (v0.40.3) для языка программирования Elixir

© 2014 Chris McCord
Licensed under the MIT License.
https://phoenix.hexdocs.pm/custom_error_pages.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API