Plug 路由器是否需要 match/dispatch 管道?

Does a Plug router need a match/dispatch pipeline?

我有一个路由器模块,可以将请求转发到其他路由器。 在这个路由器中,我有一个由 plug(:match)plug(:dispatch) 组成的管道。

defmodule Example.Router do
  use Plug.Router

  plug(:match)
  plug(:dispatch)

  forward("/check", to: Example.Route.Check)

  get("/", do: send_resp(conn, 200, "router"))
end

在第二个模块中,我有相同的管道:

defmodule Example.Route.Check do
  use Plug.Router

  plug(:match)
  plug(:dispatch)

  get "/", do: send_resp(conn, 200, "ok")
end

我在这里看到的问题是,在所有 Plug 路由器中,我似乎总是需要 plug(:match)plug(:dispatch)。所以我有以下问题:

  1. 这真的有必要吗?
  2. 是否所有路由器都需要在具有路由的同一文件中包含管道?

是的,两个插头总是需要的:

  • :match 插件负责匹配 传入请求到路由器中定义的路由之一。

  • :dispatch插件负责最终处理匹配路由中的请求。


这里明显的问题是:

Why not just do it automatically, since this needs to be done for every request?

  1. 对于初学者来说,这是因为有一种设计理念,即显式而不是隐式 ].

  2. 其次,更重要的是,插件按照定义的顺序执行。这使开发人员可以完全控制传入请求的处理方式。


例如,您可能希望在路由匹配之前检查 Authorization header 并从那里停止或继续请求。或者您可能希望在一个单独的过程中更新页面浏览量,一旦路由匹配但在处理之前。另一种常见的情况是parse a JSON request一条路由被匹配后

您可以通过自定义管道来完成所有这些以及更多操作:

defmodule Example.Router do
  use Plug.Router

  plug(CheckRateLimit)
  plug(VerifyAuthHeader)
  plug(:match)
  plug(LogWebRequest)
  plug(Plug.Parsers, parsers: [:json], ...)
  plug(:dispatch)

  # ...
end

将匹配的路由转发到其他路由器的能力可以使您的网络服务器更加复杂。例如,您可以检查基本路由器中的 API 速率限制,将 /admin 路由转发到单独的 AuthorizedRouter 并在匹配这些路由之前将自定义 VerifyAuthHeader 插头放在那里.

虽然@Sheharyar 的回答是绝对正确的,但我想补充一点,您可能会通过引入自己的辅助宏来干:

defmodule Example.Route.Common do
  defmacro __using__(opts \ []) do
    quote do
      use Plug.Router

      plug(:match)
      plug(:dispatch)
    end
  end
end

并像这样使用它:

defmodule Example.Route.Check do
  use Example.Route.Common

  get "/", do: send_resp(conn, 200, "ok")
end

opts 参数可用于精细配置包含的插件。

Kernel.use/2.