Rails allows you to specify middlewares for specifying a route just like the Phoenix pipeline
In phoenix framework
a conveyor, we can enable the option middlewares for some routes, such as:
defmodule HelloPhoenix.Router do
use HelloPhoenix.Web, :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 "/", HelloPhoenix do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
end
scope "/api", HelloPhoenix do
pipe_through :api
end
end
if the request is from /api
, will only run the plug :accepts, ["json"]
middleware
if the request is from /
, will trigger a session, flash, ... etc. middlewares
how to do it on rails if i use grape
to build api and rails to create web page and provide intermediate middleware between each other?
source to share
Unlike Phoenix apps, you cannot (easily) change which middleware is used to query in Rails. The best way to get this behavior in a Rails application is to make the controllers for a specific route inherit from a common base controller, and then define the behavior of those specific controllers in that base controller.
Using the above example of routes /api
traversing various "middleware" you can have a controller like this:
module API
class BaseController < ActionController::API
# things unique to API routes go here
end
end
Inside this controller, you can write callbacks before_action
that provide things like:
- Incoming requests only JSON
- Requests are authenticated using a specific token
Then you can inherit this controller for all API controllers:
module API
class PostsController < API::BaseController
end
end
With regular controllers, you can do all the usual things:
class ApplicationController < ActionController::Base
# things unique to non-api routes go here
end
And then:
class PostsController < ApplicationController
end
You could of course segment it more; perhaps you might have a different route, for example /accounts/1/posts
where you have to be authenticated as a user of that account to view messages. You can use the same approach:
module Accounts
class BaseController < ActionController::Base
# find account, check if the user is authenticated, etc.
end
end
and
module Accounts
class PostsController < Accounts::BaseController
end
end
So, in short: Rails doesn't let you do this at the routing level (easily), but you can do the same at the controller level.
source to share
The solution I was looking for was something big along this line:
application.rb:
# after Bundler.require(...)
require_relative '../lib/engines/website/lib/website'
Library / Engines / Site / Library / website.rb:
require_relative "website/engine"
module Website; end
Library / engines / site / Library / site / engine.rb:
module Website
class Engine < ::Rails::Engine
middleware.use ActionDispatch::Cookies
middleware.use ActionDispatch::Session::CookieStore
middleware.use ActionDispatch::Flash
end
end
config /routes.rb:
mount Website::Engine => "/website"
And everything for a website goes into a typical directory structure in the engine directory:
lib
engines
website
app
assets
...
controllers
...
views
...
config
routes.rb
lib
website
website.rb
source to share
I'm sure you can achieve this result by configuring the Rack middleware stack, but it won't be configurable in a rails application. Sometimes it is enough to have unnecessary middleware for some routes.
Pretty much every application I've seen uses the same middleware for every request.
And yes, Phoenix behaves better here, Rails behaves in earlier versions, so it's hard to change things now.
source to share