Подтвердить что ты не робот

Phoenix - контроллер с несколькими рендерами

Попытка создать приложение с 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

что означало бы создание двух контроллеров, которые могут иметь такое же поведение, за исключением того, что он будет отображать HTML с конвейером браузера и, скажем, JSON, для конвейера api.

Я думал, может быть, что-то вроде Rails respond_to do |format| ...

4b9b3361

Ответ 1

Я бы не рекомендовал его (я бы рекомендовал иметь два контроллера и переместить вашу логику в другой модуль, который вызывается обоими контроллерами), но это можно сделать. Вы можете совместно использовать контроллер, но для обеспечения правильного типа ответа (html/json) вам по-прежнему нужен отдельный конвейер.

Следующее будет использовать один и тот же контроллер и просмотр, но отображать json или html в зависимости от маршрута. "/" является html, "/api" является json.

Router:

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 в конце URL-адреса - я бы рекомендовал использовать разные URL-адреса для вашего API и сайта.

Ответ 2

Как сказал Газлер, вам, вероятно, лучше всего отнестись к отдельным конвейерам, но что-то вроде этого можно приятно провести с помощью сопоставления с шаблонами на тех же самых действиях контроллера:

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

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

Или, если тела функций одинаковы, и вы хотите сделать только шаблон на основе заголовков accept, вы можете сделать:

def show(conn, params) do
  # ...

  render conn, :show
end

Передача атома в качестве имени шаблона заставит phoenix проверить заголовки accept и отобразить шаблон .json или .html.