【问题标题】:Phoenix - controller with multiple renderPhoenix - 具有多个渲染的控制器
【发布时间】:2015-08-17 22:01:03
【问题描述】:

尝试使用 Elixir + Phoenix 创建一个应用程序,该应用程序将能够处理“浏览器”和“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| ...

【问题讨论】:

    标签: elixir phoenix-framework


    【解决方案1】:

    我不会推荐它(我会推荐有两个控制器并将你的逻辑移动到两个控制器调用的不同模块中),但它可以完成。您可以共享一个控制器,但您仍然需要一个单独的管道来确保设置正确的响应类型(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
    

    然后,您可以在网址末尾使用 ?format=json 指定格式 - 不过,我建议您为 API 和网站使用不同的网址。

    【讨论】:

    • 你不需要插入格式,你可以为模板使用一个原子,并且接受头将用于呈现正确的格式。
    • @ChrisMcCord 谢谢 - 我尝试了几件事,但无法让内容类型标头工作。我会更新我的答案。
    • @gazler 如果多个控制器是解决方案,我们应该如何命名它们以使它们不同但又分开? API 控制器应该进入 controllers/API/user_controller.exs 吗?
    【解决方案2】:

    正如 Gazler 所说,拥有单独的管道可能会为您提供最好的服务,但这样的事情可以通过对相同控制器操作的模式匹配来愉快地完成:

    def show(conn, %{"format" => "html"} = params) do
      # ...
    end
    
    def show(conn, %{"format" => "json"} = params) do
      # ...
    end
    

    或者如果函数体相同,并且您只想根据接受头渲染模板,您可以这样做:

    def show(conn, params) do
      # ...
    
      render conn, :show
    end
    

    将 atom 作为模板名称传递将导致 phoenix 检查接受标头并呈现 .json.html 模板。

    【讨论】:

    • 看起来传入的params 中没有format?自这个答案以来有什么变化吗?
    • 这个答案似乎不再有效。但是,您可以像 def index(%{private: %{phoenix_format: "json"}} = conn, params) do 那样进行模式匹配。我不知道这是安全的还是建议的。
    • 而不是直接从private 获取它(你几乎不应该这样做)你应该使用get_format(conn)hexdocs.pm/phoenix/1.6.5/Phoenix.Controller.html#get_format/1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-25
    相关资源
    最近更新 更多