基于负载的 Phoenix 路由
Phoenix routing based on the payload
我正在 Elixir/Phoenix 中实现后端 API。出于安全原因,决定在 URL 上尽可能少地显示,以便将要执行的操作嵌入到 POST 请求的 JSON 有效负载中。例如,我们不会使用类似 REST 的请求 https://my.server.com/api/get_user/42
,而是在 https://my.server.com/api/
上使用以下负载发出 POST:
{
"action" : "get_user",
"params" : {
"id" : 42
}
}
到目前为止,我的路由器是这样的:
scope "/api", AlaaarmWeb do
pipe_through :api
post "/", ApiController, :process
match :*, "/*path", ApiController, :error_404
end
我有一堆 process
函数在 params
地图上进行模式匹配:
def process(conn, params = %{"action" => "get_user"}) do
# ... get the user from the DB in resp, and sed back
json(conn, resp)
end
它工作正常,但代码不是很清楚:一个大文件,使用相同的控制器,ApiController
,而有一个 UserApiController
来管理用户会更清晰, a ProductApiController
用于管理产品等..
所以,我想知道是否有一种方法可以根据有效负载的内容来选择要调用的模块和函数,而不仅仅是 URL。
不,我不认为 Phoenix.Router 可以根据负载进行路由。这必须在控制器中完成。
但是您仍然可以按照您的描述制作单独的控制器 files/modules,并且 use
它们都在“主”ApiController 中。
如果您已将结构定义为通过负载传递的“action”和“params”键值对,并且有许多请求需要过滤并根据路径委托给不同的控制器,那么您应该能够在端点中定义了这样的插头。 ex 插入前 YourApp.Router
call
plug Plug.Session, @session_options
plug UrlFromPayloadAction, []
plug WhosebugApiWeb.Router
这个插件会改变路径信息,所以我们修改的 url 会被路由器解析,我们可以定义控制器,比如 post "/get_user/:id", ApiUserController, :process
和其他基于定义的动作。
defmodule UrlFromPayloadAction do
import Plug.Conn
def init(default), do: default
def call(%Plug.Conn{params: %{"action" => action,
"params" => %{ "id" => id }
}} = conn, _default) do
conn = conn
|> assign(:original_path_info, conn.path_info)
|> assign(:original_request_path, conn.request_path)
%Plug.Conn{conn | path_info: conn.path_info ++ [ "#{action}", "#{id}" ] , request_path: conn.request_path <> "/#{action}/#{id}" }
end
def call(conn, _default), do: conn
end
这更像是一种解决这个问题的可变方法,它可能违背像长生不老药这样的功能框架的一般哲学
我正在 Elixir/Phoenix 中实现后端 API。出于安全原因,决定在 URL 上尽可能少地显示,以便将要执行的操作嵌入到 POST 请求的 JSON 有效负载中。例如,我们不会使用类似 REST 的请求 https://my.server.com/api/get_user/42
,而是在 https://my.server.com/api/
上使用以下负载发出 POST:
{
"action" : "get_user",
"params" : {
"id" : 42
}
}
到目前为止,我的路由器是这样的:
scope "/api", AlaaarmWeb do
pipe_through :api
post "/", ApiController, :process
match :*, "/*path", ApiController, :error_404
end
我有一堆 process
函数在 params
地图上进行模式匹配:
def process(conn, params = %{"action" => "get_user"}) do
# ... get the user from the DB in resp, and sed back
json(conn, resp)
end
它工作正常,但代码不是很清楚:一个大文件,使用相同的控制器,ApiController
,而有一个 UserApiController
来管理用户会更清晰, a ProductApiController
用于管理产品等..
所以,我想知道是否有一种方法可以根据有效负载的内容来选择要调用的模块和函数,而不仅仅是 URL。
不,我不认为 Phoenix.Router 可以根据负载进行路由。这必须在控制器中完成。
但是您仍然可以按照您的描述制作单独的控制器 files/modules,并且 use
它们都在“主”ApiController 中。
如果您已将结构定义为通过负载传递的“action”和“params”键值对,并且有许多请求需要过滤并根据路径委托给不同的控制器,那么您应该能够在端点中定义了这样的插头。 ex 插入前 YourApp.Router
call
plug Plug.Session, @session_options
plug UrlFromPayloadAction, []
plug WhosebugApiWeb.Router
这个插件会改变路径信息,所以我们修改的 url 会被路由器解析,我们可以定义控制器,比如 post "/get_user/:id", ApiUserController, :process
和其他基于定义的动作。
defmodule UrlFromPayloadAction do
import Plug.Conn
def init(default), do: default
def call(%Plug.Conn{params: %{"action" => action,
"params" => %{ "id" => id }
}} = conn, _default) do
conn = conn
|> assign(:original_path_info, conn.path_info)
|> assign(:original_request_path, conn.request_path)
%Plug.Conn{conn | path_info: conn.path_info ++ [ "#{action}", "#{id}" ] , request_path: conn.request_path <> "/#{action}/#{id}" }
end
def call(conn, _default), do: conn
end
这更像是一种解决这个问题的可变方法,它可能违背像长生不老药这样的功能框架的一般哲学