Rails源码阅览(六)ActionController:Dispatcher_用户请求在rails中的处理流程(1)
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)
====结束====
=== ===
== ==
= =
| |