看看Rails的request/response源码吧,非常有趣,有些方法非常实用
1,request.rb:
[code]
module ActionController
class AbstractRequest
def method
@request_method ||= (!parameters[:_method].blank? && @env['REQUEST_METHOD'] == 'POST') ?
parameters[:_method].to_s.downcase.to_sym :
@env['REQUEST_METHOD'].downcase.to_sym
@request_method == :head ? :get : @request_method
end
def get?
method == :get
end
def post?
method == :post
end
def put?
method == :put
end
def delete?
method == :delete
end
def head?
@env['REQUEST_METHOD'].downcase.to_sym = :head
end
def content_type
@content_type ||=
begin
content_type = @env['CONTENT_TYPE'].to_s.downcase
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
case x_post_format.to_s.downcase
when 'yaml'
content_type = 'application/x-yaml'
when 'xml'
content_type = 'application/xml'
end
end
Mime::Type.lookup(content_type)
end
end
def accepts
@accepts ||=
if @env['HTTP_ACCEPT'].to_s.strip.empty?
[ content_type, Mime::ALL ]
else
Mime::Type.parse(@env['HTTP_ACCEPT'])
end
end
def xml_http_request?
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
end
alias xhr? :xml_http_request?
def remote_ip
return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP'
if @env.include? 'HTTP_X_FORWARDED_FOR' then
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
ip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
end
return remote_ips.first.strip unless remote_ips.empty?
end
@env['REMOTE_ADDR']
end
def domain(tld_length = 1)
return nil if !/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(host).nil? or host.nil?
host.split('.').last(1 + tld_length).join('.')
end
def subdomains(tld_length = 1)
return [] unless host
parts = host.split('.')
parts[0..-(tld_length+2)]
end
def request_uri
if uri = @env['REQUEST_URI']
(%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
else
script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
uri = @env['PATH_INFO']
uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
uri << '?' << env_qs
end
@env['REQUEST_URI'] = uri
end
end
def protocol
ssl? ? 'https://' : 'http://'
end
def ssl?
@env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
end
def port
@port_as_int ||= @env['SERVER_PORT'].to_i
end
def cookies
end
def session
end
end
end
[/code]
2,response.rb:
[code]
module ActionController
class AbstractResponse
DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
attr_accessor :body, :headers, :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params, :layout
def initialize
@body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], []
end
def content_type=(mime_type)
@headers["Content-Type"] = charset ? "#{mime_type}; charset=#{charset}" : mime_type
end
def content_type
content_type = String(@headers["Content-Type"]).split(";")[0]
content_type.blank? ? nil : content_type
end
def charset=(encoding)
@headers["Content-Type"] = "#{content_type || "text/html"}; charset=#{encoding}"
end
def charset
charset = String(@headers["Content-Type"]).split(";")[1]
charset.blank? ? nil : charset.strip.split("=")[1]
end
def redirect(to_url, permanently = false)
@headers["Status"] = "302 Found" unless @headers["Status"] == "301 Moved Permanently"
@headers["Location"] = to_url
@body = "<html><body>You are being <a href=\"#{to_url}\">redirected</a>.</body></html>"
end
end
end
[/code]
3,cgi_process.rb:
[code]
module ActionController
class Base
def self.process_cgi(cgi = CGI.new, session_options = {})
new.process_cgi(cgi, session_options)
end
def process_cgi(cgi, session_options = {})
process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out
end
end
class CgiRequest < AbstractRequest
attr_accessor :cgi, :session_options
DEFAULT_SESSION_OPTIONS = {
:database_manager => CGI::Session::PStore,
:prefix => "ruby_sess.",
:session_path => "/"
} unless const_defined?(:DEFAULT_SESSION_OPTIONS)
def initialize(cgi, session_options = {})
@cgi = cgi
@session_options = session_options
@env = @cgi.send(:env_table)
super()
end
def cookies
@cgi.cookies.freeze
end
def session
unless defined?(@session)
if @session_options == false
@session = Hash.new
else
stale_session_check! do
case value = session_options_with_string_keys['new_session']
when true
@session = new_session
when false
begin
@session = CGI::Session.new(@cgi, session_options_with_string_keys)
rescue ArgumentError
@session = Hash.new
end
when nil
@session = CGI::Session.new(@cgi, session_options_with_string_keys)
else
raise ArgumentError, "Invalid new_session option: #{value}"
end
@session['__valid_session']
end
end
end
@session
end
end
class CgiResponse < AbstractResponse
def initialize(cgi)
@cgi = cgi
super()
end
def out(output = $stdout)
convert_content_type!
set_content_length!
output.binmode if output.respond_to?(:binmode)
output.sync = false if output.respond_to?(:sync=)
begin
output.write(@cgi.header(@headers))
if @cgi.send(:env_table)['REQUEST_METHOD'] == 'HEAD'
return
elsif @body.respond_to?(:call)
output.flush if output.respond_to?(:flush)
@body.call(self, output)
else
output.write(@body)
end
output.flush if output.respond_to?(:flush)
rescue Errno::EPIPE, Errno::ECONNRESET
end
end
end
end
[/code]
Controller的入口方法process_cgi调用了process方法,参数为CgiRequest对象和CgiResponse对象,process处理action调用和模板render,然后返回response.out方法的返回值
CgiResponse的out方法则调用output.write(@body),即write response.body并返回给浏览器端,这就是一个Rails URL访问的完整流程
request里有很多有用的方法,request.method、request.xhr?、request.remote_ip、request.subdomains、request.session、request.cookies等都是我们常用的
1,request.rb:
[code]
module ActionController
class AbstractRequest
def method
@request_method ||= (!parameters[:_method].blank? && @env['REQUEST_METHOD'] == 'POST') ?
parameters[:_method].to_s.downcase.to_sym :
@env['REQUEST_METHOD'].downcase.to_sym
@request_method == :head ? :get : @request_method
end
def get?
method == :get
end
def post?
method == :post
end
def put?
method == :put
end
def delete?
method == :delete
end
def head?
@env['REQUEST_METHOD'].downcase.to_sym = :head
end
def content_type
@content_type ||=
begin
content_type = @env['CONTENT_TYPE'].to_s.downcase
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
case x_post_format.to_s.downcase
when 'yaml'
content_type = 'application/x-yaml'
when 'xml'
content_type = 'application/xml'
end
end
Mime::Type.lookup(content_type)
end
end
def accepts
@accepts ||=
if @env['HTTP_ACCEPT'].to_s.strip.empty?
[ content_type, Mime::ALL ]
else
Mime::Type.parse(@env['HTTP_ACCEPT'])
end
end
def xml_http_request?
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
end
alias xhr? :xml_http_request?
def remote_ip
return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP'
if @env.include? 'HTTP_X_FORWARDED_FOR' then
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
ip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
end
return remote_ips.first.strip unless remote_ips.empty?
end
@env['REMOTE_ADDR']
end
def domain(tld_length = 1)
return nil if !/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(host).nil? or host.nil?
host.split('.').last(1 + tld_length).join('.')
end
def subdomains(tld_length = 1)
return [] unless host
parts = host.split('.')
parts[0..-(tld_length+2)]
end
def request_uri
if uri = @env['REQUEST_URI']
(%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
else
script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
uri = @env['PATH_INFO']
uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
uri << '?' << env_qs
end
@env['REQUEST_URI'] = uri
end
end
def protocol
ssl? ? 'https://' : 'http://'
end
def ssl?
@env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
end
def port
@port_as_int ||= @env['SERVER_PORT'].to_i
end
def cookies
end
def session
end
end
end
[/code]
2,response.rb:
[code]
module ActionController
class AbstractResponse
DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
attr_accessor :body, :headers, :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params, :layout
def initialize
@body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], []
end
def content_type=(mime_type)
@headers["Content-Type"] = charset ? "#{mime_type}; charset=#{charset}" : mime_type
end
def content_type
content_type = String(@headers["Content-Type"]).split(";")[0]
content_type.blank? ? nil : content_type
end
def charset=(encoding)
@headers["Content-Type"] = "#{content_type || "text/html"}; charset=#{encoding}"
end
def charset
charset = String(@headers["Content-Type"]).split(";")[1]
charset.blank? ? nil : charset.strip.split("=")[1]
end
def redirect(to_url, permanently = false)
@headers["Status"] = "302 Found" unless @headers["Status"] == "301 Moved Permanently"
@headers["Location"] = to_url
@body = "<html><body>You are being <a href=\"#{to_url}\">redirected</a>.</body></html>"
end
end
end
[/code]
3,cgi_process.rb:
[code]
module ActionController
class Base
def self.process_cgi(cgi = CGI.new, session_options = {})
new.process_cgi(cgi, session_options)
end
def process_cgi(cgi, session_options = {})
process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out
end
end
class CgiRequest < AbstractRequest
attr_accessor :cgi, :session_options
DEFAULT_SESSION_OPTIONS = {
:database_manager => CGI::Session::PStore,
:prefix => "ruby_sess.",
:session_path => "/"
} unless const_defined?(:DEFAULT_SESSION_OPTIONS)
def initialize(cgi, session_options = {})
@cgi = cgi
@session_options = session_options
@env = @cgi.send(:env_table)
super()
end
def cookies
@cgi.cookies.freeze
end
def session
unless defined?(@session)
if @session_options == false
@session = Hash.new
else
stale_session_check! do
case value = session_options_with_string_keys['new_session']
when true
@session = new_session
when false
begin
@session = CGI::Session.new(@cgi, session_options_with_string_keys)
rescue ArgumentError
@session = Hash.new
end
when nil
@session = CGI::Session.new(@cgi, session_options_with_string_keys)
else
raise ArgumentError, "Invalid new_session option: #{value}"
end
@session['__valid_session']
end
end
end
@session
end
end
class CgiResponse < AbstractResponse
def initialize(cgi)
@cgi = cgi
super()
end
def out(output = $stdout)
convert_content_type!
set_content_length!
output.binmode if output.respond_to?(:binmode)
output.sync = false if output.respond_to?(:sync=)
begin
output.write(@cgi.header(@headers))
if @cgi.send(:env_table)['REQUEST_METHOD'] == 'HEAD'
return
elsif @body.respond_to?(:call)
output.flush if output.respond_to?(:flush)
@body.call(self, output)
else
output.write(@body)
end
output.flush if output.respond_to?(:flush)
rescue Errno::EPIPE, Errno::ECONNRESET
end
end
end
end
[/code]
Controller的入口方法process_cgi调用了process方法,参数为CgiRequest对象和CgiResponse对象,process处理action调用和模板render,然后返回response.out方法的返回值
CgiResponse的out方法则调用output.write(@body),即write response.body并返回给浏览器端,这就是一个Rails URL访问的完整流程
request里有很多有用的方法,request.method、request.xhr?、request.remote_ip、request.subdomains、request.session、request.cookies等都是我们常用的