代码之家  ›  专栏  ›  技术社区  ›  script

将Phoenix请求路径与数据库中定义的路由匹配

  •  8
  • script  · 技术社区  · 6 年前

    当我们发送请求时,在conn结构中有当前路径信息。比如补丁请求。

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

    我正在为基于数据库中路径信息的用户授权编写一个插件。在数据库中,路径存储如下:

    "v1/users/:id"
    

    这是我们从跑步中获得的路径 mix phx.routes .有没有可能我能得到 "v1/users/:id" 而不是 "v1/users/2" 对于当前路径?所以我可以将它与数据库中存储的路径匹配。

    有什么工作吗?

    2 回复  |  直到 6 年前
        1
  •  1
  •   Gabriel Prá    6 年前

    一个非常简单的方法是手动替换相关键路径中的值。

    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 .

        2
  •  0
  •   Sheharyar    6 年前

    假设数据库中存储了用户定义的路由列表,那么可以手动检查传入请求的路径是否与其中一个匹配。 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