之前在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方法,