Rails源码阅读(六)ActionController::Dispatcher和接下来的处理流程_用户请求在rails中的处理流程(1)
--紧接这一节:Rails源码阅读(二)_script/server
前面的分析小回顾:
用户的请求,经过rack的栈后,终于到了rails的ActionController::Dispatcher,这也是一个rack的实现,因此,请求会去调用ActionController::Dispatcher的call方法,并且应该返回一个样子的数组:[status, header, body]
=ActionController::Dispatcher的new代码:
# DEPRECATE: Remove arguments, since they are only used by CGI
def initialize(output = $stdout, request = nil, response = nil)
@output = output
build_middleware_stack if @@cache_classes
end
#1 @@cache_classes 用处
@@cache_classes = true #这个是默认的,但是在develop环境中,设置为false
如果设置为false的话,每次请求都会重新加载???,这个在开发环境非常有用。
#2 build_middleware_stack做什么
private
def build_middleware_stack
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
end
@@middleware从字面上也可以看出来,是存储middleware们的。
@@middleware是MiddlewareStack的一个实例
@@middleware的赋值代码:
cattr_accessor :middleware
self.middleware = MiddlewareStack.new do |middleware|
middlewares = File.join(File.dirname(__FILE__), "middlewares.rb")
middleware.instance_eval(File.read(middlewares))
end
# middlewares.rb文件内存储的是需要固定使用的rack们
use "Rack::Lock", :if => lambda {
!ActionController::Base.allow_concurrency
}
use "ActionController::Failsafe"
use lambda { ActionController::Base.session_store },
lambda { ActionController::Base.session_options }
use "ActionController::ParamsParser"
use "Rack::MethodOverride"
use "Rack::Head"
use "ActionController::StringCoercion"
# @@middleware是MiddlewareStack的一个实例,这个实例的build方法返回一个rack,这个rack是已经排列好stack顺序的rack
def build(app)
active.reverse.inject(app) { |a, e| e.build(a) } #看了后面就知道这里为啥用reverse了
end
每一个在middlewares.rb里的rack,都会执行下面的方法
def build(app)
if block
klass.new(app, *build_args, &block)
else
klass.new(app, *build_args)
end
end
这样的结果是:每一个在middlewares.rb文件里的rack,都会new一个实例出来
build的结果:组成了一个很好的rack栈,先new的在顶端,app在最底端。返回的是顶端的实例。
是这样做到的:
#ActionController::ParamsParser.new和.call的代码是这样的:
def initialize(app)
@app = app
end
def call(env) #先执行自己
if params = parse_formatted_parameters(env)
env["action_controller.request.request_parameters"] = params
end
@app.call(env) #后执行别人,即new的时候传入的app
end
这样最后实现了目的:new的时候,持有了下一层的句柄@app,先执行自己的代码,最后执行@app.call,这样就实现了过滤栈的效果。这种方式不错~~
@app = @@middleware.build(lambda { |env| self.dup._call(env) })在这里传入的一个block,会在放在stack的最底端。
@app就是最终的rack栈的句柄
有个小问题:self即inner_app = ActionController::Dispatcher.new对象,为啥dup呢???
分析完了new代码,接下来执行的应该是call代码:
=call的代码:
def call(env)
if @@cache_classes
@app.call(env)
else
Reloader.run do
# When class reloading is turned on, we will want to rebuild the
# middleware stack every time we process a request. If we don't
# rebuild the middleware stack, then the stack may contain references
# to old classes metal classes, which will b0rk class reloading.
build_middleware_stack
@app.call(env)
end
end
end
这里解释了在develop环境下时,@@cache_classes是关闭的,走else分支,每个请求都重新build rack-stack。
请求执行的过程为:
当有一个请求来的时候,会执行call,
执行call的时候,会build出一个rack栈,
先执行之前加入到rack栈中的rack,最后执行block,代码为:self.dup._call(env)
#_call的代码:
def _call(env)
@env = env
dispatch
end
这里才进入主操作:就是dispatch方法!!
def dispatch
begin
run_callbacks :before_dispatch
Routing::Routes.call(@env)
rescue Exception => exception
if controller ||= (::ApplicationController rescue Base)
controller.call_with_exception(@env, exception).to_a
else
raise exception
end
ensure
run_callbacks :after_dispatch, :enumerator => :reverse_each
end
end
在这里,dispach做的真正的操作交给了Routing::Routes.call(@env)
总结:
ActionController::Dispatcher这个rack的call操作做了哪些操作:
#1 根据配置build出了一个rack栈(并没有用栈,怎么实现的上面讲了)
#2 在develop中,@@cache_classes是false,每次请求都重新build一次rack-stack
#3 dispach操作,交给了ActionController::Routing::Routes.call(@env)
====结束====
=== ===
== ==
= =
| |