Phoenix.VerifiedRoutes поведение
Исходный кодПредоставляет генерацию маршрутов с проверкой во время компиляции.
Использование макроса sigil_p позволяет проверять пути и URL-адреса во всём приложении во время компиляции на соответствие маршрутизаторам Phoenix. Например, следующие варианты использования путей и URL-адресов:
~H"""
<a href={~p"/sessions/new"}>Log in</a>
"""
redirect(to: url(~p"/posts/#{post}"))
Будут проверены на соответствие стандартным определениям Phoenix.Router:
get "/posts/:post_id", PostController, :show post "/sessions/new", SessionController, :create
Для несовпадающих маршрутов компилятор выдаст предупреждения:
warning: no route path for AppWeb.Router matches "/postz/#{post}"
lib/app_web/controllers/post_controller.ex:100: AppWeb.PostController.show/2
Кроме того, интерполируемые значения ~p кодируются с помощью протокола Phoenix.Param. Например, структура %Post{} в вашем приложении может реализовывать протокол Phoenix.Param, чтобы создавать пути на основе слагов, а не идентификаторов. Это позволяет использовать ~p"/posts/#{post}" вместо ~p"/posts/#{post.slug}" во всём приложении. Дополнительные сведения см. в документации Phoenix.Param.
Наконец, в проверяемых маршрутах поддерживаются и строки запроса — как в традиционной форме:
~p"/posts?page=#{page}"
Так и в виде ключевого списка или карты значений:
params = %{page: 1, direction: "asc"}
~p"/posts?#{params}"
Как и сегменты пути, параметры строки запроса корректно кодируются для URL и могут интерполироваться непосредственно в строку ~p.
Чтобы упростить сравнение URL во время тестов (например, при использовании assert_redirect/3), параметры запроса будут отсортированы. Это регулируется параметром конфигурации phoenix: [sort_verified_routes_query_params: true].
Что насчёт именованных маршрутов?
Многие веб-фреймворки и ранние версии Phoenix предоставляли функцию под названием «именованные маршруты». Идея заключается в том, что при определении маршрутов в веб-приложении им можно также присвоить имена. В Phoenix это делалось следующим образом:
get "/login", SessionController, :create, as: :login
Теперь можно было сгенерировать маршрут с помощью функции login_path.
Именованные маршруты позволяют избежать жёсткого задания маршрутов в шаблонах: если написать <a href="/login">, а затем изменить маршрутизатор, ссылка будет указывать на несуществующую страницу. Используя login_path, мы гарантируем, что ссылка всегда будет указывать на действительный URL в нашем маршрутизаторе. Однако у именованных маршрутов есть недостаток — необходимость косвенного обращения: при чтении кода не сразу понятно, какой URL будет сгенерирован. Кроме того, если у вас есть URL и вы хотите добавить его в шаблон, нужно выполнить обратный поиск и найти его имя в маршрутизаторе. В конечном счёте именованные маршруты — это произвольные имена, которые разработчикам приходится запоминать, что увеличивает когнитивную нагрузку.
Проверяемые маршруты решают эту проблему, позволяя записывать маршруты так, как мы видим их в браузере, и при этом использовать сигил ~p, чтобы гарантировать их существование на этапе компиляции. Они устраняют необходимость косвенного обращения к именованным маршрутам, сохраняя при этом гарантии.
В любом случае, если для части приложения нужны функции, похожие на именованные маршруты, помните, что для достижения того же результата можно использовать возможности Elixir. Например, можно определить несколько функций как именованные маршруты и повторно использовать их в разных модулях:
def login_path, do: ~p"/login"
def user_home_path(user), do: ~p"/users/#{user.username}"
Параметры
Чтобы проверять маршруты в модулях приложения, таких как контроллеры, шаблоны и представления, используйте use Phoenix.VerifiedRoutes, который поддерживает следующие параметры:
-
:router— обязательный маршрутизатор, относительно которого проверяются пути~p -
:endpoint— необязательная конечная точка для генерации URL -
:statics— необязательный список статических каталогов, которые следует считать проверяемыми путями -
:path_prefixes— необязательный список префиксов путей, добавляемых ко всем сгенерированным путям. Дополнительные сведения см. в разделе «Префиксы путей»
Например:
use Phoenix.VerifiedRoutes, router: AppWeb.Router, endpoint: AppWeb.Endpoint, statics: ~w(images)
Генерация маршрутов на основе соединения/сокета
Большинство потребностей приложения в генерации путей и URL можно удовлетворить с помощью ~p и url/1: вся необходимая для построения пути или URL информация предоставляется во время компиляции из данных Endpoint и Router, переданных в use Phoenix.VerifiedRoutes.
Тем не менее в некоторых случаях требуются path/2, path/3, url/2 и url/3:
-
Когда значения
%Plug.Conn{},%Phoenix.LiveSocket{}или%URI{}во время выполнения определяют формирование пути или URL, что происходит в следующих случаях:-
Phoenix.Controller.put_router_url/2используется для переопределения URL конечной точки -
Phoenix.Controller.put_static_url/2используется для переопределения статического URL конечной точки
-
Когда модуль Router отличается от переданного в
use Phoenix.VerifiedRoutes, например в коде библиотеки или приложения, использующего несколько маршрутизаторов. В таких случаях модуль маршрутизатора можно явно передать вpath/3иurl/3.
Отслеживание предупреждений
Все статические сегменты пути должны начинаться с косой черты, а между динамическими интерполяциями должен находиться статический сегмент, чтобы маршрут проверялся без предупреждений. Например, предположим, что у вас есть два следующих маршрута:
get "/media/posts/:id" get "/media/images/:id"
Следующий маршрут будет проверен и выдаст предупреждение, поскольку он не соответствует маршрутизатору:
~p"/media/post/#{post}"
Однако маршрут ниже проверен не будет: сегмент «post» является динамическим:
type = "post"
~p"/media/#{type}/#{post}"
Если вам требуется генерировать динамические URL, которые статически определены в маршрутизаторе, это хороший повод реорганизовать код и вынести их в одну или несколько функций, например posts_path/1 и images_path/1.
Как и при любом другом предупреждении компиляции, компилятор Elixir выдаёт предупреждение при каждом изменении файла, в котором находится ~p, а также при изменении маршрутизатора.
Локализованные маршруты и префиксы путей
Приложениям, которым необходима поддержка интернационализации (i18n) и локализации (l10n), часто требуется реализовать её на уровне URL. В таких случаях можно выбрать один из нескольких подходов.
Один из вариантов — реализовать i18n на уровне домена. Можно использовать example.com (в этом случае локаль определяется по HTTP-заголовку «Accept-Language»), en.example.com, en-GB.example.com и так далее. В этом случае плагин анализирует хост и HTTP-заголовки и соответствующим образом вызывает Gettext.get_locale/1. Главное преимущество такого подхода — отсутствие необходимости менять маршруты в приложении: проверяемые маршруты продолжат работать без изменений.
Однако в некоторых приложениях локаль добавляют в качестве префикса URL:
scope "/:locale" do get "/posts" get "/images" end
Для таких случаев VerifiedRoutes позволяет настроить параметр path_prefixes — список сегментов, добавляемых в начало URL. Например:
use Phoenix.VerifiedRoutes,
router: AppWeb.Router,
endpoint: AppWeb.Endpoint,
path_prefixes: [{Gettext, :get_locale, []}]
В приведённом выше примере "/#{Gettext.get_locale()}" добавляется в начало каждого пути и URL, созданного с помощью ~p. Если на вашем сайте есть несколько URL, которым не нужен префикс локали, рекомендуем определить их в отдельном модуле и использовать Phoenix.VerifiedRoutes без параметра префикса:
defmodule UnlocalizedRoutes do
use Phoenix.VerifiedRoutes,
router: AppWeb.Router,
endpoint: AppWeb.Endpoint
# Since :path_prefixes was not declared,
# the code below won't prepend the locale and still be verified
def root, do: ~p"/"
end
Наконец, для ещё более сложных случаев, когда требуется локализовать URL целиком, рассмотрите такие проекты, как routex и ex_cldr_routes.
Использование с пользовательскими плагинами
Иногда при динамической маршрутизации мы перенаправляем запросы в пользовательские плагины. Такие динамические маршрутизаторы можно научить поддерживать mix phx.routes и проверяемые маршруты во время компиляции, реализовав поведение Phoenix.VerifiedRoutes. Например:
defmodule MyApp.LocaleRouter do
use Plug.Router
@behaviour Phoenix.VerifiedRoutes
# custom routing rules
# for displaying in `mix phx.routes`
def formatted_routes(plug_opts) do
for locale <- supported_locales(plug_opts) do
%{verb: "GET", path: "/#{locale}/*subpath"}
end
end
def verified_route?(plug_opts, path) do
plug_opts
|> supported_locales()
|> Enum.any?(fn locale ->
Enum.at(path, 0) == locale
end)
end
end Краткое описание
Типы
- formatted_routes(plug_opts)
Возвращает необходимую информацию о маршрутах для отображения в
mix phx.routes.- verified_route?(plug_opts, list)
Возвращает
true, если путь проверен, и false в противном случае.
Обратные вызовы
- path(conn_or_socket_or_endpoint_or_uri, sigil_p)
Генерирует путь маршрутизатора с проверкой маршрута.
- path(conn_or_socket_or_endpoint_or_uri, router, sigil_p)
Генерирует путь маршрутизатора с проверкой маршрута.
- sigil_p(route, extra)
Генерирует путь маршрутизатора с проверкой маршрута.
- static_integrity(conn_or_socket_or_endpoint, path)
Генерирует хеш целостности статического ресурса по пути к его файлу.
- static_path(conn_or_socket_or_endpoint_or_uri, path)
Генерирует путь к статическому ресурсу по пути к его файлу.
- static_url(conn_or_socket_or_endpoint, path)
Генерирует URL статического ресурса по пути к его файлу.
- unverified_path(conn_or_socket_or_endpoint_or_uri, router, path, params \\ %{})
Возвращает путь с соответствующими префиксами имени скрипта без проверки.
- unverified_url(conn_or_socket_or_endpoint_or_uri, path, params \\ %{})
Возвращает URL конечной точки, сформированный из пути, без проверки.
- url(sigil_p)
Генерирует URL маршрутизатора с проверкой маршрута.
- url(conn_or_socket_or_endpoint_or_uri, sigil_p)
Генерирует URL маршрутизатора с проверкой маршрута на основе соединения, сокета или URI.
- url(conn_or_socket_or_endpoint_or_uri, router, sigil_p)
Генерирует URL с проверкой маршрута на основе соединения, сокета или URI и маршрутизатора.
Функции
Типы
formatted_route()Исходный код
@type formatted_route() :: %{verb: String.t(), path: String.t(), label: String.t()} plug_opts()Исходный код
@type plug_opts() :: any()
Обратные вызовы
formatted_routes(plug_opts)Исходный код
@callback formatted_routes(plug_opts()) :: [formatted_route()]
Возвращает необходимую информацию о маршрутах для отображения в mix phx.routes.
Параметр plug_opts обычно передаётся только в том случае, если маршрутизатор подключён внутри Phoenix.Router. В противном случае по умолчанию используется [].
verified_route?(plug_opts, list)Исходный код
@callback verified_route?(plug_opts(), [String.t()]) :: boolean()
Возвращает true, если путь проверен, и false в противном случае.
Параметр plug_opts обычно передаётся только в том случае, если маршрутизатор подключён внутри Phoenix.Router. В противном случае по умолчанию используется [].
Функции
path(conn_or_socket_or_endpoint_or_uri, sigil_p)Исходный код
Генерирует путь маршрутизатора с проверкой маршрута.
Дополнительные сведения см. в разделе sigil_p/2.
Выдаёт предупреждение, если указанный путь не соответствует маршрутизатору, заданному в use Phoenix.VerifiedRoutes или атрибуте модуля @router.
Примеры
import Phoenix.VerifiedRoutes
redirect(to: path(conn, ~p"/users/top"))
redirect(to: path(conn, ~p"/users/#{@user}"))
~H"""
<.link href={path(@uri, "/users?page=#{@page}")}>profile</.link>
<.link href={path(@uri, "/users?#{@params}")}>profile</.link>
""" path(conn_or_socket_or_endpoint_or_uri, router, sigil_p)Исходный код
Генерирует путь маршрутизатора с проверкой маршрута.
Дополнительные сведения см. в разделе sigil_p/2.
Выдаёт предупреждение, если указанный путь не соответствует маршрутизатору, заданному аргументом router.
Примеры
import Phoenix.VerifiedRoutes
redirect(to: path(conn, MyAppWeb.Router, ~p"/users/top"))
redirect(to: path(conn, MyAppWeb.Router, ~p"/users/#{@user}"))
~H"""
<.link href={path(@uri, MyAppWeb.Router, "/users?page=#{@page}")}>profile</.link>
<.link href={path(@uri, MyAppWeb.Router, "/users?#{@params}")}>profile</.link>
""" sigil_p(route, extra)Исходный код
Генерирует путь маршрутизатора с проверкой маршрута.
Интерполируемые именованные параметры кодируются с помощью протокола Phoenix.Param.
Выдаёт предупреждение, если указанный путь не соответствует маршрутизатору, заданному в use Phoenix.VerifiedRoutes или атрибуте модуля @router.
Примеры
use Phoenix.VerifiedRoutes, endpoint: MyAppWeb.Endpoint, router: MyAppWeb.Router
redirect(to: ~p"/users/top")
redirect(to: ~p"/users/#{@user}")
~H"""
<.link href={~p"/users?page=#{@page}"}>profile</.link>
<.link href={~p"/users?#{@params}"}>profile</.link>
""" static_integrity(conn_or_socket_or_endpoint, path)Исходный код
Генерирует хеш целостности статического ресурса по пути к его файлу.
Дополнительные сведения см. в разделе Phoenix.Endpoint.static_integrity/1.
Примеры
iex> static_integrity(conn, "/assets/js/app.js") "813dfe33b5c7f8388bccaaa38eec8382" iex> static_integrity(socket, "/assets/js/app.js") "813dfe33b5c7f8388bccaaa38eec8382" iex> static_integrity(AppWeb.Endpoint, "/assets/js/app.js") "813dfe33b5c7f8388bccaaa38eec8382"
static_path(conn_or_socket_or_endpoint_or_uri, path)Исходный код
Генерирует путь к статическому ресурсу по пути к его файлу.
Дополнительные сведения см. в разделе Phoenix.Endpoint.static_path/1.
Примеры
iex> static_path(conn, "/assets/js/app.js")
"/assets/js/app-813dfe33b5c7f8388bccaaa38eec8382.js"
iex> static_path(socket, "assets/js/app.js")
"/assets/js/app-813dfe33b5c7f8388bccaaa38eec8382.js"
iex> static_path(AppWeb.Endpoint, "assets/js/app.js")
"/assets/js/app-813dfe33b5c7f8388bccaaa38eec8382.js"
iex> static_path(%URI{path: "/subresource"}, "/assets/js/app.js")
"/subresource/assets/js/app-813dfe33b5c7f8388bccaaa38eec8382.js" static_url(conn_or_socket_or_endpoint, path)Исходный код
Генерирует URL статического ресурса по пути к его файлу.
Дополнительные сведения см. в разделах Phoenix.Endpoint.static_url/0 и Phoenix.Endpoint.static_path/1.
Примеры
iex> static_url(conn, "/assets/js/app.js") "https://example.com/assets/js/app-813dfe33b5c7f8388bccaaa38eec8382.js" iex> static_url(socket, "/assets/js/app.js") "https://example.com/assets/js/app-813dfe33b5c7f8388bccaaa38eec8382.js" iex> static_url(AppWeb.Endpoint, "/assets/js/app.js") "https://example.com/assets/js/app-813dfe33b5c7f8388bccaaa38eec8382.js"
unverified_path(conn_or_socket_or_endpoint_or_uri, router, path, params \\ %{})Исходный код
Возвращает путь с соответствующими префиксами имени скрипта без проверки.
Примеры
iex> unverified_path(conn, AppWeb.Router, "/posts") "/posts" iex> unverified_path(conn, AppWeb.Router, "/posts", page: 1) "/posts?page=1"
unverified_url(conn_or_socket_or_endpoint_or_uri, path, params \\ %{})Исходный код
Возвращает URL конечной точки, сформированный из пути, без проверки.
Примеры
iex> unverified_url(conn, "/posts") "https://example.com/posts" iex> unverified_url(conn, "/posts", page: 1) "https://example.com/posts?page=1"
url(sigil_p)Исходный код
Генерирует URL маршрутизатора с проверкой маршрута.
Дополнительные сведения см. в разделе sigil_p/2.
Выдаёт предупреждение, если указанный путь не соответствует маршрутизатору, заданному в use Phoenix.VerifiedRoutes или атрибуте модуля @router.
Примеры
use Phoenix.VerifiedRoutes, endpoint: MyAppWeb.Endpoint, router: MyAppWeb.Router
redirect(to: url(conn, ~p"/users/top"))
redirect(to: url(conn, ~p"/users/#{@user}"))
~H"""
<.link href={url(@uri, "/users?#{[page: @page]}")}>profile</.link>
"""
Маршрутизатор также можно указать, если требуется проверять маршруты для маршрутизатора, отличного от переданного в use Phoenix.VerifiedRoutes:
redirect(to: url(conn, OtherRouter, ~p"/users"))
Перенаправленные маршруты также разрешаются автоматически. Например, предположим, что в главном маршрутизаторе у вас есть путь перенаправления к маршрутизатору администратора:
defmodule AppWeb.Router do ... forward "/admin", AppWeb.AdminRouter end defmodule AppWeb.AdminRouter do ... get "/users", AppWeb.Admin.UserController end
Пути перенаправления в главном маршрутизаторе приложения будут проверяться как обычно, например ~p"/admin/users".
url(conn_or_socket_or_endpoint_or_uri, sigil_p)Исходный код
Генерирует URL маршрутизатора с проверкой маршрута на основе соединения, сокета или URI.
Дополнительные сведения см. в разделе url/1.
url(conn_or_socket_or_endpoint_or_uri, router, sigil_p)Исходный код
Генерирует URL с проверкой маршрута на основе соединения, сокета или URI и маршрутизатора.
Дополнительные сведения см. в разделе url/1.
© 2014 Chris McCord
Licensed under the MIT License.
https://phoenix.hexdocs.pm/Phoenix.VerifiedRoutes.html