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 с помощью конвейера browser и, скажем, JSON, для конвейера api.

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


person Kernael    schedule 03.06.2015    source источник


Ответы (2)


Я бы не рекомендовал это (я бы рекомендовал иметь два контроллера и переместить вашу логику в другой модуль, который вызывается обоими контроллерами), но это можно сделать. Вы можете совместно использовать контроллер, но вам все равно понадобится отдельный конвейер, чтобы обеспечить правильный тип ответа (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 в конце URL-адреса - однако я бы рекомендовал использовать разные URL-адреса для вашего API и сайта.

person Gazler    schedule 03.06.2015
comment
Вам не нужно интерполировать формат, вы можете использовать атом для шаблона, и заголовки accept будут использоваться для отображения правильного формата. - person Chris McCord; 03.06.2015
comment
@ChrisMcCord спасибо - Я попробовал пару вещей и не смог заставить работать заголовок типа контента. Я обновлю свой ответ. - person Gazler; 03.06.2015
comment
@gazler, если решением является несколько контроллеров, как мы должны назвать их пространством, чтобы они были разными, но раздельными? должны ли контроллеры API входить в controllers / API / user_controller.exs? - person DogEatDog; 07.10.2016

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

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

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

person Chris McCord    schedule 03.06.2015
comment
Не похоже, что в переданном params есть format? Что-то изменилось после этого ответа? - person Constant Meiring; 04.03.2017
comment
Этот ответ больше не кажется действительным. Однако вы можете сопоставить шаблон, например def index(%{private: %{phoenix_format: "json"}} = conn, params) do. Я понятия не имею, безопасно ли это или предлагается. - person d_rail; 04.08.2017