Ruby On Rails-2.0.2源代码分析(4)-寻找Contro…
2008-04-02 02:27:10来源: 阅读 ()
-
前言
经过一番试验和考虑...一,我尝试了一些思维导图工具(MindMapper,FREEMIND),但我始终没有找到一种好的方式将自己学习Rails源代码的思路表述出来,就此作罢(顺便问问,有研究思维导图的同学么?能否推荐两个自己觉得用起来比较顺手的工具)。二,不再打算整理代码运行顺序图,对不熟悉Rails源代码的同学们来说,这个图可能的确没什么帮助,甚至会把人搞晕。我现在打算从Rails源代码功能点的角度出发,根据具体功能点,结合Rails源代码进行学习,整理,总结。如果某些源代码比较复杂,牵涉类比较繁多,我仍然打算整理一个类图,从一个高的层次了解系统内部对象的关系。
前面三篇文章,我们看到了Rails启动的大致功能和流程,包括初始化多种环境变量,初始化Route表,启动Web服务器开始侦听客户端请求。。。那么接下来,当然是开门迎客,等待客户端(浏览器)的请求,并进行处理,最终将结果返回客户端(浏览器)呈现。那么熟悉Rails的同学都知道,首先,Rails必须根据客户端的一个请求,决定将要执行哪个Controller的哪个Action,这也是本文的主要目的。
-
寻找Controller
首先,我们先来看一看Rails通过客户端请求,查找Controller的大致流程图
(一)生成DisapatchServlet实例,开始服务吧
源代码:gems/rails-2.0.2/lib/webrick_server.rb
在第一篇文章,讲解Rails的启动时,我提到webrick_server.rb中定义了DisapatchServlet类,此类启动了WEBrick,开始侦听客户端请求。当有客户端请求到达时,会生成一个DispatchServlet实例,具体代码如下:
- class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
- def initialize(server, options) #:nodoc:
- @server_options = options
- @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root])
- # Change to the RAILS_ROOT, since Webrick::Daemon.start does a Dir::cwd("/")
- # OPTIONS['working_directory'] is an absolute path of the RAILS_ROOT, set in railties/lib/commands/servers/webrick.rb
- Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory'])
- super
- end
- ...
- end
class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet def initialize(server, options) #:nodoc: @server_options = options @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root]) # Change to the RAILS_ROOT, since Webrick::Daemon.start does a Dir::cwd("/") # OPTIONS['working_directory'] is an absolute path of the RAILS_ROOT, set in railties/lib/commands/servers/webrick.rb Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory']) super end ... end
初始化参数server是web服务器的类型,当然,在我的环境中是WEBRick::HTTPServer。option是一个hash,包含了一些列的环境参数,这里,我将一些比较重要的参数罗列出来:
名称 | 类型 | 参考值 |
port | Fixnum | 3000 |
ip | String | 0.0.0.0(因为我是本机操作) |
environment | String | development |
charset | String | UTF-8 |
working_directory | String | D:\Project\Ruby\blog |
(二)是否存在相应html
源代码:gems/rails-2.0.2/lib/webrick_server.rb
第一步生成了Servlet实例,并且,开始执行service方法,我们先来看看service方法的具体内容:
- class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
- def service(req, res) #:nodoc:
- unless handle_file(req, res)
- begin
- REQUEST_MUTEX.lock unless ActionController::Base.allow_concurrency
- unless handle_dispatch(req, res)
- raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
- end
- ensure
- unless ActionController::Base.allow_concurrency
- REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked?
- end
- end
- end
- end
- ...
- end
class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet def service(req, res) #:nodoc: unless handle_file(req, res) begin REQUEST_MUTEX.lock unless ActionController::Base.allow_concurrency unless handle_dispatch(req, res) raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." end ensure unless ActionController::Base.allow_concurrency REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked? end end end end ... end
此方法算是处理一个Request的最高层次描述,首先是方法handle_file。这个方法会使用初始化生成的FileHandler对象,查找针对客户端请求的path,在RAILS_ROOT/public目录下是否存在相应的html。例如客户端的请求是http://localhost:3000/posts,那么首先Rails就使用FileHandler查找在public根目录下面是否存在posts.html,如果存在的话,则直接向客户端呈现这个html,如果不存在,OK,开始寻找Controller吧。
(这里,值得一提是并发控制,默认情况下,Rails只允许一次dispatch一个request,当然,我们可以通过在程序配置文件中设置ActionController::Base.allow_concurrency来改变这个默认的行为。)
(我想你应该知道很多Rails书籍提到过,如果你在routes.rb中通过map.root :controller=>'posts'的方式,使得当用户通过http://www.yoursite.com访问站点时,显示相应的功能页面。但是你必须把public下的index.html删除掉,就是这个原因。)
(handle_file源代码不列出,因为他十分简单,只是调用FileHandler的相应方法,而WEBRick暂不在研究范围内。)
(三)开始Dispatch吧
源代码:gems/rails-2.0.2/lib/webrick_server.rb
gems/actionpack-2.0.2/lib/action_controller/dispatcher.rb
第二步说了,如果没有相应的html存在的话,Rails将执行Dispatch过程。我们先来看一看handle_dispatch方法:
- def handle_dispatch(req, res, origin = nil) #:nodoc:
- data = StringIO.new
- Dispatcher.dispatch(
- CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")),
- ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS,
- data
- )
- ...
- end
def handle_dispatch(req, res, origin = nil) #:nodoc: data = StringIO.new Dispatcher.dispatch( CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")), ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, data ) ... end
这里,可以看到Dispatch的主角Dispatcher对象开始登场了。要执行dispatch,首先生成一个CGI对象(默认CGI类型是“query”,并且将环境配置传递给CGI对象,包括:主机名称,查询字符串,字符集,Path信息...等,以及默认的Session管理方式),其中的data表示对用户的返回数据(StringIO请参考相应的API)。然后执行Dispatcher的类方法dispatch。此方法内容如下:
- class Dispatcher
- class << self
- # Backward-compatible class method takes CGI-specific args. Deprecated
- # in favor of Dispatcher.new(output, request, response).dispatch.
- def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
- new(output).dispatch_cgi(cgi, session_options)
- end
- ...
- end
class Dispatcher class << self # Backward-compatible class method takes CGI-specific args. Deprecated # in favor of Dispatcher.new(output, request, response).dispatch. def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout) new(output).dispatch_cgi(cgi, session_options) end ... end
此类方法将生成一个Dispatcher实例,并调用其dispatch_cgi实例方法(从前面的方法调用,我想不难看出每一个参数是什么)。我们继续接着看dispatch_cgi方法:
- def dispatch_cgi(cgi, session_options)
- if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new }
- @request = CgiRequest.new(cgi, session_options)
- @response = CgiResponse.new(cgi)
- dispatch
- end
- rescue Exception => exception
- failsafe_rescue exception
- end
def dispatch_cgi(cgi, session_options) if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new } @request = CgiRequest.new(cgi, session_options) @response = CgiResponse.new(cgi) dispatch end rescue Exception => exception failsafe_rescue exception end
前面也有request和reponse,这里又生成了一个request和response。我是这样理解的,前面handle_dispatch接收的req和res是“原生”的对象----WEBRick::HTTPRequest和WEBRick::HTTPResponse(是WEBRick和Rails的通讯方式),而这里的request和response是CgiRequest和CgiResponse对象,是针对Dispatch的通讯(CgiRequest和CgiResponse的细节这里先略过,我们看看主流程)。有了request和response对象,真正的dispatch过程开始了:
- def dispatch
- run_callbacks :before
- handle_request
- rescue Exception => exception
- failsafe_rescue exception
- ensure
def dispatch run_callbacks :before handle_request rescue Exception => exception failsafe_rescue exception ensure标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash