想知道当前的Rails应用塞入了些什么middleware,可用rake middleware打印出来,用rake -W middleware找到其任务定义如下

# lib/rails/tasks/middleware.rake
desc "Prints out your Rack middleware stack"                                                                                                                                                                                            
task middleware: :environment do
  Rails.configuration.middleware.each do |middleware|
    puts "use #{middleware.inspect}"
  end
  puts "run #{Rails.application.class.name}.routes"
end


那么这些middleware是何时塞入的呢?先检查下middleware对象是怎么定义的

[9] pry(main)> Rails.configuration.middleware.method(:each).source_location
=> ["/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/actionpack-5.1.2/lib/action_dispatch/middleware/stack.rb", 48]


找到其源码,加上pry跟踪一下

module ActionDispatch
  class MiddlewareStack

    def initialize(*args)
      require 'pry'
      binding.pry
      @middlewares = []
      yield(self) if block_given?
    end


再一次rake middleware
    
From: /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/actionpack-5.1.2/lib/action_dispatch/middleware/stack.rb @ line 46 ActionDispatch::MiddlewareStack#initialize:

   43: def initialize(*args)
   44:   require 'pry'
   45:   binding.pry
=> 46:   @middlewares = []
   47:   yield(self) if block_given?
   48: end

[1] pry(#)> backtrace
--> #0  ActionDispatch::MiddlewareStack.initialize(*args#Array) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/actionpack-5.1.2/lib/action_dispatch/middleware/stack.rb:46
    ͱ-- #1  Class.new(*args) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application/default_middleware_stack.rb:13
    #2  Rails::Application::DefaultMiddlewareStack.build_stack at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application/default_middleware_stack.rb:13
    #3  Rails::Application.default_middleware_stack at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application.rb:501
    #4  block in Rails::Application.block in app at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/engine.rb:506
    ͱ-- #5  Thread::Mutex.synchronize at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/engine.rb:504
    #6  Rails::Application.app at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/engine.rb:504
    #7  block in block in  at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application/finisher.rb:45
    ͱ-- #8  BasicObject.instance_exec(*args) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/initializable.rb:30
    #9  Rails::Initializable::Initializer.run(*args#Array) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/initializable.rb:30
    #10 block in Rails::Initializable.block in run_initializers(group#Symbol, *args#Array) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/initializable.rb:59
    #11 block in #.block in tsort_each(each_node#Method, each_child#Method) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:228
    #12 block (2 levels) in #.block (2 levels) in each_strongly_connected_component(each_node#Method, each_child#Method) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:350
    #13 #.each_strongly_connected_component_from(node#Rails::Initializable::Initializer, each_child#Method, id_map#Hash, stack#Array) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:431
    #14 block in #.block in each_strongly_connected_component(each_node#Method, each_child#Method) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:349
    ͱ-- #15 Rails::Initializable::Collection.each at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:347
    ͱ-- #16 Method.call(*args) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:347
    #17 #.each_strongly_connected_component(each_node#Method, each_child#Method) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:347
    #18 #.tsort_each(each_node#Method, each_child#Method) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:226
    #19 TSort.tsort_each(&block#Proc) at /home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/tsort.rb:205
    #20 Rails::Initializable.run_initializers(group#Symbol, *args#Array) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/initializable.rb:58
    #21 Rails::Application.initialize!(group#Symbol) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application.rb:353
    #22 <top (required)=""> at /home/z/test_rails/school/config/environment.rb:5
    ͱ-- #23 Kernel.require(file) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application.rb:329
    #24 Rails::Application.require_environment! at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application.rb:329
    #25 block in Rails::Application.block in run_tasks_blocks(app#School::Application) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.1.2/lib/rails/application.rb:445
    #26 block in Rake::Task.block in execute(args#Rake::TaskArguments) at /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/task.rb:250
    # ...</top>


railsI相关的源头可追溯至如下代码

# railties-5.1.2/lib/rails/application.rb
module Rails
  class Application < Engine

    def run_tasks_blocks(app)
      railties.each { |r| r.run_tasks_blocks(app) }
      super
      require "rails/tasks"
      task :environment do
        ActiveSupport.on_load(:before_initialize) { config.eager_load = false }

        require_environment!                                                                                                                                                                                                            
      end
    end

    def require_environment!
      environment = paths["config/environment"].existent.first
      require environment if environment
    end


不过本次的目的还是看middleware的具体加载,如下

# railties-5.1.2/lib/rails/application/default_middleware_stack.rb
def build_stack
  ActionDispatch::MiddlewareStack.new.tap do |middleware|
    if config.force_ssl
      middleware.use ::ActionDispatch::SSL, config.ssl_options
    end

    middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header

    if config.public_file_server.enabled
      headers = config.public_file_server.headers || {}

      middleware.use ::ActionDispatch::Static, paths["public"].first, index: config.public_file_server.index_name, headers: headers
    end

    if rack_cache = load_rack_cache
      require "action_dispatch/http/rack_cache"
      middleware.use ::Rack::Cache, rack_cache
    end

    if config.allow_concurrency == false
      # User has explicitly opted out of concurrent request
      # handling: presumably their code is not threadsafe

      middleware.use ::Rack::Lock
    end

    middleware.use ::ActionDispatch::Executor, app.executor

    middleware.use ::Rack::Runtime
    middleware.use ::Rack::MethodOverride unless config.api_only
    middleware.use ::ActionDispatch::RequestId
    middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies

    middleware.use ::Rails::Rack::Logger, config.log_tags
    middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
    middleware.use ::ActionDispatch::DebugExceptions, app, config.debug_exception_response_format

    unless config.cache_classes
      middleware.use ::ActionDispatch::Reloader, app.reloader
    end

    middleware.use ::ActionDispatch::Callbacks
    middleware.use ::ActionDispatch::Cookies unless config.api_only

    if !config.api_only && config.session_store
      if config.force_ssl && config.ssl_options.fetch(:secure_cookies, true) && !config.session_options.key?(:secure)
        config.session_options[:secure] = true
      end
      middleware.use config.session_store, config.session_options
      middleware.use ::ActionDispatch::Flash
    end                                                                                                                                                                                                                           

    middleware.use ::Rack::Head
    middleware.use ::Rack::ConditionalGet
    middleware.use ::Rack::ETag, "no-cache"
  end
end


而如果要加入自己的middleware,可以自己调用以下方法

# actionpack-5.1.2/lib/action_dispatch/middleware/stack.rb
module ActionDispatch
  class MiddlewareStack

    def insert(index, klass, *args, &block)
      index = assert_index(index, :before)
      middlewares.insert(index, build_middleware(klass, args, block))
    end

    alias_method :insert_before, :insert

    def insert_after(index, *args, &block)
      index = assert_index(index, :after)
      insert(index + 1, *args, &block)
    end

    def swap(target, *args, &block)
      index = assert_index(target, :before)
      insert(index, *args, &block)
      middlewares.delete_at(index + 1)
    end

    def delete(target)
      middlewares.delete_if { |m| m.klass == target }
    end

    def use(klass, *args, &block)
      middlewares.push(build_middleware(klass, args, block))
    end


配置可加在config/application.rb文件,或参考其注释的建议

module School
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    config.middleware.use Rack::ShowStatus
  end
end