Обработка ошибок и исключений
Исходный кодКак и в любом другом коде Elixir, исключения могут возникать на протяжении жизненного цикла LiveView. На этой странице описано, как LiveView обрабатывает ошибки на разных этапах.
Ожидаемые сценарии
В этом разделе мы поговорим об ошибках, которые могут возникнуть в вашем приложении. Например, ожидаемо, что пользователь может заполнить форму некорректными данными. В LiveView мы обычно обрабатываем такие ситуации, сохраняя состояние формы в assigns LiveView и отображая клиенту соответствующее сообщение об ошибке.
Для этого мы также можем использовать сообщения flash. Например, представьте, что у вас есть страница для управления всеми «Участниками команды» в организации. Однако если в организации остался только один участник, ему не следует разрешать выходить из неё. Эту ситуацию можно обработать с помощью flash-сообщений:
if MyApp.Org.leave(socket.assigns.current_org, member) do
{:noreply, socket}
else
{:noreply, put_flash(socket, :error, "last member cannot leave organization")}
end
Однако можно возразить, что если последний участник организации не может выйти из неё, то, возможно, лучше вообще не показывать кнопку «Выйти» в интерфейсе, когда в организации остался только один участник.
Если кнопка не отображается в интерфейсе, попытка выполнить действие «выйти», когда в организации остался только один участник, является неожиданным сценарием. Это означает, что приведённый выше код можно переписать так:
true = MyApp.Org.leave(socket.assigns.current_org, member)
{:noreply, socket}
Если leave не возвращает true, Elixir вызовет исключение MatchError. Или можно предоставить функцию leave!, которая вызывает определённое исключение:
MyApp.Org.leave!(socket.assigns.current_org, member)
{:noreply, socket}
Но что произойдёт с LiveView в случае исключения? Давайте поговорим о неожиданных сценариях.
Неожиданные сценарии
Разработчики Elixir обычно пишут код с явными проверками. Это означает, что если мы ожидаем, что leave всегда возвращает true, мы можем явно сопоставить результат с образцом, как сделали выше:
true = MyApp.Org.leave(socket.assigns.current_org, member)
{:noreply, socket}
Если leave завершается неудачей и возвращает false, возникает исключение. Разработчики Elixir часто используют исключения для обработки неожиданных сценариев в приложениях Phoenix.
Например, если вы создаёте приложение, в котором пользователь может состоять в одной или нескольких организациях, при открытии страницы организации можно проверить, есть ли у пользователя к ней доступ:
organizations_query = Ecto.assoc(socket.assigns.current_user, :organizations) Repo.get!(organizations_query, params["org_id"])
Приведённый выше код формирует запрос, который возвращает все организации, в которых состоит текущий пользователь, а затем проверяет, что указанная org_id принадлежит этому пользователю. Если такой org_id нет или у пользователя нет к ней доступа, Repo.get! вызовет исключение Ecto.NoResultsError.
При обычном запросе контроллера это исключение преобразуется в исключение 404 и отображается как пользовательская страница ошибки, как описано здесь. LiveView реагирует на исключения тремя разными способами — в зависимости от этапа жизненного цикла.
Исключения при HTTP-монтировании
При первом обращении к LiveView на сервер отправляется обычный HTTP-запрос, который обрабатывается LiveView. Вызывается callback mount, после чего отображается страница. Любое исключение на этом этапе перехватывается, записывается в журнал и преобразуется в страницу исключения средствами Phoenix error views — точно так же, как и при работе с контроллерами.
Исключения при монтировании подключённого LiveView
Если первоначальный HTTP-запрос выполнен успешно, LiveView подключается к серверу через соединение с сохранением состояния, обычно WebSocket. При этом на сервере запускается долгоживущий лёгкий процесс Elixir, который вызывает callback mount и отображает обновлённую версию страницы.
Исключение на этом этапе приведёт к сбою процесса LiveView, о котором будет сделана запись в журнале. Заметив сбой, клиент полностью перезагрузит страницу. В результате mount будет вызван повторно при обычном HTTP-запросе (как в сценарии, описанном в предыдущем подразделе).
Иными словами, при ошибках LiveView перезагрузит страницу, и она завершится сбоем так, как если бы LiveView изначально не участвовал в её отображении.
Исключения после монтирования подключённого LiveView
После монтирования и подключения LiveView любая ошибка приведёт к сбою процесса LiveView, о котором будет сделана запись в журнале. Заметив ошибку, клиент повторно смонтирует LiveView через соединение с сохранением состояния, не перезагружая страницу (как в сценарии, описанном в предыдущем подразделе). Если повторное монтирование пройдёт успешно, LiveView вернётся в рабочее состояние, обновит страницу и покажет пользователю актуальную информацию.
Например, предположим, что два пользователя пытаются одновременно выйти из организации. В этом случае оба видят кнопку «Выйти», но вызов нашей функции leave завершится успешно только для одного из них:
true = MyApp.Org.leave(socket.assigns.current_org, member)
{:noreply, socket}
При возникновении исключения клиент повторно смонтирует LiveView. После повторного монтирования код обнаружит, что в организации остался только один пользователь, и больше не будет показывать кнопку «Выйти». Иными словами, повторное монтирование часто обновляет состояние страницы, позволяя автоматически обрабатывать исключения.
Обратите внимание: решать, проверять ли результат функции leave условием if или просто утверждать, что она возвращает true, — исключительно вам. Если вероятность того, что все одновременно выйдут из организации, невелика, можно считать такую ситуацию неожиданной. Другим разработчикам, однако, будет комфортнее явно обрабатывать подобные случаи. В обоих случаях LiveView поможет вам.
Наконец, если LiveView завершится сбоем, его текущее состояние будет потеряно. К счастью, в LiveView есть ряд механизмов и рекомендаций, которые помогут при повторном подключении показать пользователю ту же страницу, что и до сбоя. Дополнительные сведения см. в руководстве «Развёртывание и восстановление».
© 2014 Chris McCord
Licensed under the MIT License.
https://phoenix-live-view.hexdocs.pm/error-handling.html