之前在csdn写了不少关于Rails的笔记,不过觉得一个文件一个文件来读不够有条理,现在可以从更加实用和课题式的角度来读Rails代码。
以WEBrick为例子,WEBrick的请求处理代码如下:
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
处理html请求,进入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 ) header, body = extract_header_and_body(data) #从返回的data中获取相应header和body set_charset(header) assign_status(res, header) res.cookies.concat(header.delete('set-cookie') || []) header.each { |key, val| res[key] = val.join(", ") } res.body = body return true rescue => err p err, err.backtrace return false end
def extract_header_and_body(data) data.rewind data = data.read raw_header, body = *data.split(/^[\xd\xa]{2}/on, 2) header = WEBrick::HTTPUtils::parse_header(raw_header) return header, body end
ActionController::Dispatcher里的调用过程如下:
def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout) new(output).dispatch_cgi(cgi, session_options) end def dispatch_cgi(cgi, session_options) if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new } #确保能生成response对象 @request = CgiRequest.new(cgi, session_options) @response = CgiResponse.new(cgi) dispatch end rescue Exception => exception failsafe_rescue exception end
调用dispatch方法
这里有两个钩子方法before_dispatch和after_dispatch
def dispatch
@@guard.synchronize do
begin
run_callbacks :before_dispatch
handle_request
rescue Exception => exception
failsafe_rescue exception
ensure
run_callbacks :after_dispatch, :enumerator => :reverse_each
end
end
end
处理单个请求
def handle_request @controller = Routing::Routes.recognize(@request) @controller.process(@request, @response).out(@output) end
调用ActionController的process方法,process里面调用了一系列方法
# Extracts the action_name from the request parameters and performs that action. def process(request, response, method = :perform_action, *arguments) #:nodoc: initialize_template_class(response) assign_shortcuts(request, response) initialize_current_url assign_names forget_variables_added_to_assigns log_processing send(method, *arguments) assign_default_content_type_and_charset response.request = request response.prepare! unless component_request? response ensure process_cleanup end
send(method,*arguments)就是调用当前controller对应的action方法,