Сопоставить путь запроса Phoenix с маршрутом, определенным в базе данных - PullRequest
0 голосов
/ 05 декабря 2018

Когда мы отправляем запрос, у нас есть текущая информация о пути в структуре conn.Допустим, для запроса исправления.

["v1", "users", "2"] or "v1/users/2"

Я пишу плагин для авторизации пользователей на основе информации о пути в БД.В БД пути хранятся так:

"v1/users/:id"

Это путь, который мы получаем при запуске mix phx.routes.Возможно ли, что я могу получить "v1/users/:id" вместо "v1/users/2" для текущего пути?так что я могу сопоставить его с путем, хранящимся в БД.

Есть ли обходной путь?

Ответы [ 2 ]

0 голосов
/ 08 декабря 2018

Учитывая, что у вас есть список пользовательских маршрутов, хранящихся в базе данных, вы можете вручную проверить, совпадает ли путь входящего запроса с одним из них. Regex может помочь вам решить эту проблему изящно (хотя это не так эффективно, как скомпилированные маршруты):

defmodule RouteMatcher do    
  def find(routes, path) do
    Enum.find_value(routes, nil, &match(&1, path))
  end

  defp match(route, path) do
    pattern = String.replace(route, ~r/:(\w+)/, ~S"(?<\g{1}>[\w-]+)")
    regex = ~r/^#{pattern}$/

    case Regex.named_captures(regex, path) do
      nil -> nil
      map -> {route, map}
    end
  end   
end

Теперь предположим, что это список всех определенных маршрутов вбаза данных:

routes = [
  "/v1/users",
  "/v1/users/:user_id",
  "/v1/users/:user_id/posts",
  "/v1/users/:user_id/posts/:post_id",
  "/v1/users/:user_id/posts/:post_id/:comment_id",
]

Затем функция RouteMatcher.find/2 вернет первый маршрут, который соответствует заданному пути, вместе с соответствующими параметрами (если не найдено ни одного маршрута, он просто вернет nil):

RouteMatcher.find(routes, "/v1/users")
#=> {"/v1/users", %{}}

RouteMatcher.find(routes, "/v1/users/psy")
#=> {"/v1/users/:user_id", %{"user_id" => "psy"}}

RouteMatcher.find(routes, "/v1/users/psy/posts")
#=> {"/v1/users/:user_id/posts", %{"user_id" => "psy"}}

RouteMatcher.find(routes, "/v1/users/psy/posts/hello-world")
#=> {"/v1/users/:user_id/posts/:post_id", %{"post_id" => "hello-world", "user_id" => "psy"}}

RouteMatcher.find(routes, "/v1/users/psy/posts/hello-world/45")
#=> {"/v1/users/:user_id/posts/:post_id/:comment_id", %{"comment_id" => "45", "post_id" => "hello-world", "user_id" => "psy"}}

RouteMatcher.find(routes, "/unknown/route")                    
#=> nil
0 голосов
/ 08 декабря 2018

Очень хакерский способ сделать это - вручную заменить значение в пути для связанного ключа.

В структуре conn у нас есть path_info: ["v1", "users", "2"] и path_params: %{"id" => "2"}, поэтому мыможет сделать:

Enum.reduce(conn.path_params, conn.path_info, fn {key, value}, acc ->
  index = Enum.find_index(acc, fn x -> x == value end)
  List.replace_at(acc, index, ":#{key}")
end)
|> Enum.join("/")

И вывод будет v1/users/:id.

Обратите внимание, что это не удастся, если у вас есть значение параметра, которое является частью маршрута (что вряд ли произойдет).Кроме того, если у вас есть несколько параметров, которые могут принимать одно и то же значение, мы будем полагаться на порядок параметров в path_params.

...