Rails源码阅览(六)ActionController:Dispatcher_用户请求在rails中的处理流程(1)

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)

 

 

 

====结束====

===           ===

==                ==

=                     =

|                       |