Permalink
Browse files

Added new infrastructure support for REST webservices.

  By default application/xml posts are handled by creating a XmlNode object with the same name as the root element of the submitted xml. M$

 ActionController::Base.param_parsers['application/atom+xml'] = Proc.new do |data|
    node = REXML::Document.new(post)
   { node.root.name => node.root }
 end

 XmlSimple and Yaml web services were retired, ActionController::Base.param_parsers carries an example which shows how to get this functio$
 request.[formatted_post?, xml_post?, yaml_post? and post_format] were all deprecated in favor of request.content_type [Tobias Luetke]

Closes #4081


git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3777 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
Tobias Lütke
Tobias Lütke committed Mar 5, 2006
1 parent 4f00c70 commit 03d37a2d68c1940f65d5a65a51ae747955a5b075
View
@@ -1,5 +1,16 @@
*SVN*
* Added new infrastructure support for REST webservices.
By default application/xml posts are handled by creating a XmlNode object with the same name as the root element of the submitted xml. More handlers can easily be registered like this
ActionController::Base.param_parsers['application/atom+xml'] = Proc.new do |data|
node = REXML::Document.new(post)
{ node.root.name => node.root }
end
XmlSimple and Yaml web services were retired, ActionController::Base.param_parsers carries an example which shows how to get this functionality back.
request.[formatted_post?, xml_post?, yaml_post? and post_format] were all deprecated in favor of request.content_type [Tobias Luetke]
* Fixed Effect.Appear in effects.js to work with floats in Safari #3524, #3813, #3044 [Thomas Fuchs]
* Fixed that default image extension was not appended when using a full URL with AssetTagHelper#image_tag #4032, #3728 [rubyonrails@beautifulpixel.com]
@@ -36,6 +36,8 @@
require 'action_controller/base'
require 'action_controller/deprecated_redirects'
require 'action_controller/request'
require 'action_controller/deprecated_request_methods'
require 'action_controller/rescue'
require 'action_controller/benchmarking'
require 'action_controller/flash'
@@ -260,10 +260,41 @@ class Base
@@allow_concurrency = false
cattr_accessor :allow_concurrency
# Modern REST web services often need to submit complex data to the web application.
# the param_parsers hash lets you register handlers wich will process the http body and add parameters to the
# @params hash. These handlers are invoked for post and put requests.
#
# By default application/xml is enabled. a XmlNode class with the same param name as the root
# will be instanciated in the @params.
#
# Example:
#
# ActionController::Base.param_parsers['application/atom+xml'] = Proc.new do |data|
# node = REXML::Document.new(post)
# { node.root.name => node.root }
# end
#
# Note: Up until release 1.1 rails would default to using XmlSimple for such requests. To get the old
# behavior you can re-register XmlSimple as application/xml handler and enable application/x-yaml like
# this:
#
# ActionController::Base.param_parsers['application/xml'] = Proc.new do |data|
# XmlSimple.xml_in(data, 'ForceArray' => false)
# end
#
# ActionController::Base.param_parsers['application/x-yaml'] = Proc.new do |data|
# |post| YAML.load(post)
# end
#
@@param_parsers = {
'application/xml' => Proc.new { |post| node = XmlNode.from_xml(post); { node.node_name => node } },
}
cattr_accessor :param_parsers
# Template root determines the base from which template references will be made. So a call to render("test/template")
# will be converted to "#{template_root}/test/template.rhtml".
class_inheritable_accessor :template_root
# The logger is used for generating information on the action run-time (including benchmarking) if available.
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
cattr_accessor :logger
@@ -302,7 +333,7 @@ class Base
# Returns the name of the action this controller is processing.
attr_accessor :action_name
class << self
# Factory for the standard create, process loop where the controller is discarded after processing.
def process(request, response) #:nodoc:
@@ -1,5 +1,6 @@
require 'cgi'
require 'action_controller/vendor/xml_simple'
require 'action_controller/vendor/xml_node'
# Static methods for parsing the query and request parameters that can be used in
# a CGI extension class or testing in isolation.
@@ -58,12 +59,7 @@ def CGIMethods.parse_request_parameters(params)
end
def self.parse_formatted_request_parameters(format, raw_post_data)
case format
when :xml
return XmlSimple.xml_in(raw_post_data, 'ForceArray' => false)
when :yaml
return YAML.load(raw_post_data)
end
ActionController::Base.param_parsers[format].call(raw_post_data) || {}
rescue Object => e
{ "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace,
"raw_post_data" => raw_post_data, "format" => format }
@@ -64,8 +64,8 @@ def query_parameters
end
def request_parameters
if formatted_post?
CGIMethods.parse_formatted_request_parameters(post_format, @env['RAW_POST_DATA'])
if ActionController::Base.param_parsers.has_key?(content_type)
CGIMethods.parse_formatted_request_parameters(content_type, @env['RAW_POST_DATA'])
else
CGIMethods.parse_request_parameters(@cgi.params)
end
@@ -0,0 +1,34 @@
module ActionController
class AbstractRequest
# Determine whether the body of a HTTP call is URL-encoded (default)
# or matches one of the registered param_parsers.
#
# For backward compatibility, the post format is extracted from the
# X-Post-Data-Format HTTP header if present.
def post_format
case content_type
when 'application/xml'
:xml
when 'application/x-yaml'
:yaml
else
:url_encoded
end
end
# Is this a POST request formatted as XML or YAML?
def formatted_post?
post? && (post_format == :yaml || post_format == :xml)
end
# Is this a POST request formatted as XML?
def xml_post?
post? && post_format == :xml
end
# Is this a POST request formatted as YAML?
def yaml_post?
post? && post_format == :yaml
end
end
end
@@ -42,44 +42,25 @@ def head?
method == :head
end
# Determine whether the body of a POST request is URL-encoded (default),
# XML, or YAML by checking the Content-Type HTTP header:
#
# Content-Type Post Format
# application/xml :xml
# text/xml :xml
# application/x-yaml :yaml
# text/x-yaml :yaml
# * :url_encoded
# Determine whether the body of a HTTP call is URL-encoded (default)
# or matches one of the registered param_parsers.
#
# For backward compatibility, the post format is extracted from the
# X-Post-Data-Format HTTP header if present.
def post_format
@post_format ||=
if @env['HTTP_X_POST_DATA_FORMAT']
@env['HTTP_X_POST_DATA_FORMAT'].downcase.to_sym
else
case @env['CONTENT_TYPE'].to_s.downcase
when 'application/xml', 'text/xml' then :xml
when 'application/x-yaml', 'text/x-yaml' then :yaml
else :url_encoded
end
end
end
def content_type
return @content_type if @content_type
# Is this a POST request formatted as XML or YAML?
def formatted_post?
post? && (post_format == :xml || post_format == :yaml)
end
# Is this a POST request formatted as XML?
def xml_post?
post? && post_format == :xml
end
# Is this a POST request formatted as YAML?
def yaml_post?
post? && post_format == :yaml
@content_type = @env['CONTENT_TYPE'].to_s.downcase
if @env['HTTP_X_POST_DATA_FORMAT']
case @env['HTTP_X_POST_DATA_FORMAT'].downcase.to_sym
when :yaml
@content_type = 'application/x-yaml'
when :xml
@content_type = 'application/xml'
end
end
@content_type
end
# Returns true if the request's "X-Requested-With" header contains
@@ -0,0 +1,98 @@
require 'rexml/document'
# SimpleXML like xml parser. Written by leon breet from the ruby on rails Mailing list
#
class XmlNode
attr :node
def initialize(node, options = {})
@node = node
@children = {}
@raise_errors = options[:raise_errors]
end
def self.from_xml(xml_or_io)
document = REXML::Document.new(xml_or_io)
if document.root
XmlNode.new(document.root)
else
XmlNode.new(document)
end
end
def node_encoding
@node.encoding
end
def node_name
@node.name
end
def node_value
@node.text
end
def node_value=(value)
@node.text = value
end
def xpath(expr)
matches = nil
REXML::XPath.each(@node, expr) do |element|
matches ||= XmlNodeList.new
matches << (@children[element] ||= XmlNode.new(element))
end
matches
end
def method_missing(name, *args)
name = name.to_s
nodes = nil
@node.each_element(name) do |element|
nodes ||= XmlNodeList.new
nodes << (@children[element] ||= XmlNode.new(element))
end
nodes
end
def <<(node)
if node.is_a? REXML::Node
child = node
elsif node.respond_to? :node
child = node.node
end
@node.add_element child
@children[child] ||= XmlNode.new(child)
end
def [](name)
@node.attributes[name.to_s]
end
def []=(name, value)
@node.attributes[name.to_s] = value
end
def to_s
@node.to_s
end
def to_i
to_s.to_i
end
end
class XmlNodeList < Array
def [](i)
i.is_a?(String) ? super(0)[i] : super(i)
end
def []=(i, value)
i.is_a?(String) ? self[0][i] = value : super(i, value)
end
def method_missing(name, *args)
name = name.to_s
self[0].__send__(name, *args)
end
end
@@ -2,6 +2,7 @@
$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib/active_support')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
require 'yaml'
require 'test/unit'
require 'action_controller'
require 'breakpoint'
@@ -10,5 +11,4 @@
ActionController::Base.logger = nil
ActionController::Base.ignore_missing_templates = false
ActionController::Routing::Routes.reload rescue nil
ActionController::Routing::Routes.reload rescue nil
Oops, something went wrong.

0 comments on commit 03d37a2

Please sign in to comment.