首页 > 解决方案 > Phoenix:拥有相同资源的 JSON 和 HTML 表示

问题描述

我开始在教程之外将 Phoenix 用于一个小用例。到目前为止很棒,但我对以下内容有点吃惊。

我有一个资源“记录”,我想在 JSON 中的 /api/recordings 和带有模板的 /recording 访问它,结果是 HTML。

理想情况下,Ecto 表示将是唯一的,甚至控制器的一部分也会被共享?

现在我要么必须有 2 个资源 recordAPI 和 recordingHTML,要么 2 个控制器和 1 个资源。

有什么例子吗?我一直在寻找其中一个,但找不到用于同一资源的 :api 管道和 :browser 管道。

谢谢

标签: elixirphoenix-frameworkecto

解决方案


您可以使用凤凰accepts插头和两种不同视图/布局的组合

# router.ex
defmodule TestExWeb.Router do
  use TestExWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", TestExWeb do
    pipe_through :browser

    get "/recording", PageController, :index
  end

  scope "/api", TestExWeb do
    pipe_through :api

    get "/recording", PageController, :index
  end
end

如您所见,:browser使用:accepts ["html"]while:api使用:accepts ["json"]. 您可以在您的私有结构中找到conn它并在控制器中使用它,如下所示:

defmodule TestExWeb.PageController do
  use TestExWeb, :controller

  def index(%{private: %{phoenix_format: format}} = conn, _params) do
    data = "Hello World"

    render(conn, "index.#{format}", data: data)
  end
end

现在,您只需要告诉 phoenix 如何渲染您的 json,html 已经page.html.eex在布局中处理好了,所以将以下内容添加到您的page_view.ex

defmodule TestExWeb.PageView do
  use TestExWeb, :view

  def render("index.json", %{data: data}) do
    %{
      data: data
    }
  end
end

此解决方案有两个缺点:

  • 您在每个控制器中都需要此格式片段(也许您可以在“发送”响应后用插头绕过它)
  • 您正在使用 phoenix 的内部变量

推荐阅读