rake middleware
想知道当前的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