4

In phoenix framework with pipeline we can enable specify middlewares for some route, for example:

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 request from /api, will only trigger plug :accepts, ["json"] middleware

if request from /, will trigger session, flash, ...etc middlewares

how to achieve this on rails, If I am using grape for build api and rails for build web page and enable difference middleware for each other?

carpamon
  • 6,515
  • 3
  • 38
  • 51
Moon soon
  • 2,616
  • 2
  • 30
  • 51

3 Answers3

5

Unlike Phoenix applications, you cannot (easily) change what middleware is used for a request within Rails. The best way to get this kind of behaviour in a Rails application would be to make the controllers for a particular route inherit from a common base controller, and then define the behaviour for these particular controllers in that base controller.

Using the above example of the /api routes going through different "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 could write some before_action callbacks that ensure things like:

  • Inbound requests are JSON only
  • Requests are authenticated with a particular token

Then you can inherit from this controller for all API controllers:

module API
  class PostsController < API::BaseController
  end
end

With your regular controllers you can do all the regular 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; maybe you might have another route like /accounts/1/posts where you have to be authenticated as the user of that account to see the posts. You can take 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 summary: Rails doesn't let you do this on a routing level (easily), but you can accomplish the same thing at the controller level.

Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
1

The solution I was looking for was something more in this direction:

application.rb:

# after Bundler.require(...)
require_relative '../lib/engines/website/lib/website'

lib/engines/website/lib/website.rb:

require_relative "website/engine"

module Website; end

lib/engines/website/lib/website/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 the website goes in the typical directory structure under the engine directory:

lib
  engines
    website
      app
        assets
          ...
        controllers
          ...
        views
          ...
      config
        routes.rb
      lib
        website
        website.rb

Reference: Build 2 middleware stacks in Rails app

Rafael Garcia
  • 361
  • 3
  • 14
0

I'm pretty sure that you can achieve this result by configuring Rack middleware stack, but it won't be configurable in a rails application. Sometimes it's affordable to have unnecessary middleware for some routes.

Almost all apps that I saw are using same middleware for every request.

And yes, Phoenix behavior is better here, Rails behavior done in earliest versions, so it's hard to change something now.

RoM4iK
  • 733
  • 6
  • 12