Trying to create an app with Elixir + Phoenix, that would be able to handle both "browser" and "api" requests to handle its resources.
Is it possible to do it without having to do something like that :
scope "/", App do
pipe_through :browser
resources "/users", UserController
end
scope "/api", App.API as: :api do
pipe_through :api
resources "/users", UserController
end
which would mean having to create two controllers, which might have the same behavior, except that it will render HTML with the browser pipeline and, say JSON, for the api pipeline.
I was thinking maybe something like the Rails respond_to do |format| ...
As Gazler said, you are probably best served by having separate pipelines, but something like this can be pleasantly done with pattern matching on the same controller actions:
def show(conn, %{"format" => "html"} = params) do
# ...
end
def show(conn, %{"format" => "json"} = params) do
# ...
end
Or if the function bodies are the same, and you would only like to render a template based on the accept headers, you can do:
def show(conn, params) do
# ...
render conn, :show
end
Passing an atom as the template name will cause phoenix to check the accept headers and render the .json
or .html
template.
I wouldn't recommend it (I would recommend having two controllers and move your logic into a different module that is called by both controllers) but it can be done. You can share a controller, but you still need a separate pipeline to ensure the correct response type (html/json) is set.
The following will use the same controller and view, but render json or html depending on the route. "/" is html, "/api" is 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
Controller:
defmodule ScopeExample.PageController do
use ScopeExample.Web, :controller
plug :action
def index(conn, params) do
render conn, :index
end
end
View:
defmodule ScopeExample.PageView do
use ScopeExample.Web, :view
def render("index.json", _opts) do
%{foo: "bar"}
end
end
You can also share the router and have everything served by the same route if you use a router like:
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
You can then specify the format using ?format=json
at the end of the url - I would recommend going with different urls for your API and site however.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With