在 Phoenix 中动态定义顶级路由 routes.ex

Dynamically defining top level routes in Phoenix routes.ex

我想做的是根据可用于数据库中特定模型的 slug 动态路由顶级路径,例如 GitHub 对 user/org 名称的处理方式,例如 https://github.com/elixir-langhttps://github.com/phoenixframework,但我似乎无法在 PhoenixFramework 中理解它。

到目前为止我在 routes.ex 中尝试的是:

Enum.each(MyApp.Repo.all(MyApp.User), fn section ->
  get "/#{user.username}", UserController, :show, assigns: %{"username" => user.username}
end)

但我在尝试编译应用程序时遇到以下错误:

== Compilation error on file web/router.ex ==
** (ArgumentError) repo MyApp.Repo is not started, please ensure it is part of your supervision tree
    lib/ecto/query/planner.ex:91: Ecto.Query.Planner.cache_lookup/3
    lib/ecto/query/planner.ex:72: Ecto.Query.Planner.query/4
    lib/ecto/repo/queryable.ex:91: Ecto.Repo.Queryable.execute/5
    lib/ecto/repo/queryable.ex:15: Ecto.Repo.Queryable.all/4
    web/router.ex:25: (module)
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

routes.ex 是 DSL,它是在编译时解析的,而不是在运行时解析的。错误消息中的前两个词是 Compilation error。这意味着,您不能在路由中使用数据库。

相反,尝试像这样定义顶级路由:

get /:user_slug, UserController, :show

在您的控制器中,检查此用户是否存在于数据库中,如果不存在,则return 404。

我写了一篇博客 post 关于如何做到这一点:

http://www.adamcz.com/blog/pretty-urls-with-phoenix

如果数据库中已有 slug,只需在路由器文件中指定参数即可:

resources "/users", UserController, only: [:index, ...], param: "slug"

然后在您的控制器中查找该参数:

def show(conn, %{"slug" => slug}) do
  user = Repo.get_by(User, slug: slug)
  # ...
end