Phoenix - 具有多个渲染器的控制器
Phoenix - controller with multiple render
尝试使用 Elixir + Phoenix 创建一个应用程序,它将能够处理 "browser" 和 "api" 请求以处理其资源。
是否可以做到这一点而不必做那样的事情:
scope "/", App do
pipe_through :browser
resources "/users", UserController
end
scope "/api", App.API as: :api do
pipe_through :api
resources "/users", UserController
end
这意味着必须创建两个控制器,它们可能具有相同的行为,除了它将使用 browser 管道呈现 HTML 并且说 JSON,用于 api 管道。
我在想也许像 Rails respond_to do |format| ...
我不会推荐它(我会推荐有两个控制器并将您的逻辑移动到两个控制器调用的不同模块中)但它可以完成。您可以共享一个控制器,但您仍然需要一个单独的管道来确保设置正确的响应类型 (html/json)。
以下将使用相同的控制器和视图,但根据路由呈现 json 或 html。 “/”是html,“/api”是json。
路由器:
defmodule ScopeExample.Router do
use ScopeExample.Web, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", ScopeExample do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
end
scope "/api", ScopeExample do
pipe_through :api # Use the default browser stack
get "/", PageController, :index
end
end
控制器:
defmodule ScopeExample.PageController do
use ScopeExample.Web, :controller
plug :action
def index(conn, params) do
render conn, :index
end
end
查看:
defmodule ScopeExample.PageView do
use ScopeExample.Web, :view
def render("index.json", _opts) do
%{foo: "bar"}
end
end
如果您使用像这样的路由器,您还可以共享路由器并让所有内容都由同一路由提供服务:
defmodule ScopeExample.Router do
use ScopeExample.Web, :router
pipeline :browser do
plug :accepts, ["html", "json"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
end
scope "/", ScopeExample do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
end
end
然后您可以在 url 末尾使用 ?format=json
指定格式 - 我建议您为您的 API 和网站使用不同的 url .
正如 Gazler 所说,拥有单独的管道可能是您的最佳选择,但是通过在相同的控制器操作上进行模式匹配可以愉快地完成类似的事情:
def show(conn, %{"format" => "html"} = params) do
# ...
end
def show(conn, %{"format" => "json"} = params) do
# ...
end
或者如果函数体相同,而你只想渲染一个基于accept的模板headers,你可以这样做:
def show(conn, params) do
# ...
render conn, :show
end
传递一个原子作为模板名称将导致 phoenix 检查接受 headers 并呈现 .json
或 .html
模板。
尝试使用 Elixir + Phoenix 创建一个应用程序,它将能够处理 "browser" 和 "api" 请求以处理其资源。
是否可以做到这一点而不必做那样的事情:
scope "/", App do
pipe_through :browser
resources "/users", UserController
end
scope "/api", App.API as: :api do
pipe_through :api
resources "/users", UserController
end
这意味着必须创建两个控制器,它们可能具有相同的行为,除了它将使用 browser 管道呈现 HTML 并且说 JSON,用于 api 管道。
我在想也许像 Rails respond_to do |format| ...
我不会推荐它(我会推荐有两个控制器并将您的逻辑移动到两个控制器调用的不同模块中)但它可以完成。您可以共享一个控制器,但您仍然需要一个单独的管道来确保设置正确的响应类型 (html/json)。
以下将使用相同的控制器和视图,但根据路由呈现 json 或 html。 “/”是html,“/api”是json。
路由器:
defmodule ScopeExample.Router do
use ScopeExample.Web, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", ScopeExample do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
end
scope "/api", ScopeExample do
pipe_through :api # Use the default browser stack
get "/", PageController, :index
end
end
控制器:
defmodule ScopeExample.PageController do
use ScopeExample.Web, :controller
plug :action
def index(conn, params) do
render conn, :index
end
end
查看:
defmodule ScopeExample.PageView do
use ScopeExample.Web, :view
def render("index.json", _opts) do
%{foo: "bar"}
end
end
如果您使用像这样的路由器,您还可以共享路由器并让所有内容都由同一路由提供服务:
defmodule ScopeExample.Router do
use ScopeExample.Web, :router
pipeline :browser do
plug :accepts, ["html", "json"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
end
scope "/", ScopeExample do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
end
end
然后您可以在 url 末尾使用 ?format=json
指定格式 - 我建议您为您的 API 和网站使用不同的 url .
正如 Gazler 所说,拥有单独的管道可能是您的最佳选择,但是通过在相同的控制器操作上进行模式匹配可以愉快地完成类似的事情:
def show(conn, %{"format" => "html"} = params) do
# ...
end
def show(conn, %{"format" => "json"} = params) do
# ...
end
或者如果函数体相同,而你只想渲染一个基于accept的模板headers,你可以这样做:
def show(conn, params) do
# ...
render conn, :show
end
传递一个原子作为模板名称将导致 phoenix 检查接受 headers 并呈现 .json
或 .html
模板。