Rails源代码分析(14):ActionController::MimeResponds

  • 什么是MimeResponds

MimeResponds主要是为了支持Web Service请求:

比如 /people/list.html 返回一个html格式的响应

比如 /people/list.xml 返回xml格式的响应

Rails determines the desired response format from the HTTP Accept header submitted by the client.

  1.          def index
  2.            @people = Person.find(:all)
  3.       
  4.            respond_to do |format|
  5.              format.html
  6.              format.xml { render :xml => @people.to_xml }
  7.            end
  8.          end
也可以自己增加MINE Type
   Mime::Type.register "image/jpg", :jpg
这个方法我们后面再分析分析

  • 实现
里面只有一个public方法 respond_to
  1.       def respond_to(*types, &block)
  2.         raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block // 参数错误只允许传types或者block
  3.          
  4.         // 如果传入types那么构建block
  5.         block ||= lambda { |responder| types.each { |type| responder.send(type) } }
  6.         responder = Responder.new(self)
  7.         //调用responder的type指名的方法 实际上先调用method_missing
  8.         block.call(responder)
  9.         responder.respond
  10.       end
为了了解清楚实际的代码,先看看register方法:

mine_types.rb里面
  1. Mime::Type.register "*/*":all
  2. Mime::Type.register "text/plain":text, [], %w(txt)
  3. Mime::Type.register "text/html":html, %w( application/xhtml+xml ), %w( xhtml )
  4. Mime::Type.register "text/javascript":js, %w( application/javascript application/x-javascript )
  5. Mime::Type.register "text/css":css
  6. Mime::Type.register "text/calendar":ics
  7. Mime::Type.register "text/csv":csv
  8. Mime::Type.register "application/xml":xml, %w( text/xml application/x-xml )
  9. Mime::Type.register "application/rss+xml":rss
  10. Mime::Type.register "application/atom+xml":atom
  11. Mime::Type.register "application/x-yaml":yaml, %w( text/yaml )
  12. Mime::Type.register "multipart/form-data":multipart_form
  13. Mime::Type.register "application/x-www-form-urlencoded":url_encoded_form
  14. # http://www.ietf.org/rfc/rfc4627.txt
  15. Mime::Type.register "application/json":json, %w( text/x-json )
mine_type.rb里面

Mime::Type.register 方法的实现:
  1.       def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
  2.         Mime.instance_eval { const_set symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms) } 
  3.         #动态的把symbol(:html :text :js等转化为常量)
  4.         # HTML = Type.new("text/html", :html, %w( application/xhtml+xml ))
  5.         SET << Mime.const_get(symbol.to_s.upcase)
  6.         # 把这个常量放入SET中
  7.         ([string] + mime_type_synonyms).each { |string| LOOKUP[string] = SET.last } unless skip_lookup 
  8.         #所有mine_types都设置到lookup中
  9.         ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last }
  10.         # 所有扩展都设置到扩展中
  11.       end
  1.       def lookup(string)
  2.         LOOKUP[string]
  3.       end
  4.       def lookup_by_extension(extension)
  5.         EXTENSION_LOOKUP[extension]
  6.       end

Mine::Type实体的对象方法:
  1.     def initialize(string, symbol = nil, synonyms = [])
  2.       @symbol@synonyms = symbol, synonyms
  3.       @string = string
  4.     end
  5.     
  6.     def to_s
  7.       @string
  8.     end
  9.     
  10.     def to_str
  11.       to_s
  12.     end
  13.     
  14.     def to_sym
  15.       @symbol || @string.to_sym
  16.     end
  17.     def ===(list)
  18.       if list.is_a?(Array)
  19.         (@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
  20.       else
  21.         super
  22.       end
  23.     end
  24.     
  25.     def ==(mime_type)
  26.       return false if mime_type.blank?
  27.       (@synonyms + [ self ]).any? do |synonym| 
  28.         synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym 
  29.       end
  30.     end
  31.     # Returns true if Action Pack should check requests using this Mime Type for possible request forgery.  See
  32.     # ActionController::RequestForgerProtection.
  33.     def verify_request?
  34.       !@@unverifiable_types.include?(to_sym)
  35.     end
  36.     def html?
  37.       @@html_types.include?(to_sym) || @string =~ /html/
  38.     end
  39.     private
  40.       def method_missing(method, *args)
  41.         if method.to_s =~ /(/w+)/?$/
  42.           $1.downcase.to_sym == to_sym
  43.         else
  44.           super
  45.         end
  46.       end



实现
  1.     class Responder #:nodoc:
  2.       def initialize(controller)
  3.         @controller = controller
  4.         @request    = controller.request
  5.         @response   = controller.response
  6.         @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts) // 根据@request的参数:format就是扩展来查询,如果没有就是在accepts方法里获取
  7.         @order     = []
  8.         @responses = {}
  9.       end
  10.       def custom(mime_type, &block)
  11.         #如果是Mine::Type那么直接返回
  12.         #如果不是,那么lookup table中去查找
  13.         mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
  14.         @order << mime_type
  15.         # 设置一个Proc等待调用
  16.        @responses[mime_type] ||= Proc.new do
  17.           @response.template.template_format = mime_type.to_sym #返回symbol作为格式
  18.           @response.content_type = mime_type.to_s #返回to_s就是类似"text/html"这段
  19.           block_given? ? block.call : @controller.send(:render:action => @controller.action_name)
  20.           #如果没有设置block直接调用render方法 
  21.           # 例如 format.html
  22.           #如果设置那么执行block
  23.           # 例如:format.xml { render :xml => @people.to_xml }
  24.         end
  25.       end
  26.       def any(*args, &block)
  27.         if args.any?
  28.           args.each { |type| send(type, &block) }
  29.         else
  30.           custom(@mime_type_priority.first, &block) //取优先级第一个的mine_type
  31.         end
  32.       end
  33.       def method_missing(symbol, &block)
  34.         mime_constant = symbol.to_s.upcase
  35.         if Mime::SET.include?(Mime.const_get(mime_constant)) 
  36.           #如果在SET中包含了这个常量就调用custom才处理这个常量
  37.           custom(Mime.const_get(mime_constant), &block)
  38.         else
  39.           super
  40.         end
  41.       end
  42.       def respond
  43.         for priority in @mime_type_priority
  44.           if priority == Mime::ALL
  45.             @responses[@order.first].call
  46.             return
  47.           else
  48.             if @responses[priority]
  49.               @responses[priority].call
  50.               return # mime type match found, be happy and return
  51.             end
  52.           end
  53.         end
  54.         if @order.include?(Mime::ALL)
  55.           @responses[Mime::ALL].call
  56.         else
  57.           @controller.send :head:not_acceptable
  58.         end
  59.       end
  60.     end

其中Request的accepts方法如下:
  1.     def accepts
  2.       @accepts ||=
  3.         if @env['HTTP_ACCEPT'].to_s.strip.empty?
  4.           [ content_type, Mime::ALL ].compact # make sure content_type being nil is not included
  5.         else
  6.           Mime::Type.parse(@env['HTTP_ACCEPT'])
  7.         end
  8.     end


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值