mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
always send back SOAP responses as UTF-8, we can't guarantee that SOAP4R
supports any encoding sent by caller. add documentation describing how to ensure :string types don't get converted into :base64 by SOAP4R when containing non-ASCII chars and $KCODE is not set to a value. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1822 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
f29ce1f356
commit
dfc422b784
4 changed files with 57 additions and 33 deletions
|
@ -215,6 +215,25 @@ custom types and message definition types:
|
|||
The default namespace used is 'urn:ActionWebService', if you don't supply
|
||||
one.
|
||||
|
||||
|
||||
== ActionWebService and UTF-8
|
||||
|
||||
If you're going to be sending back strings containing non-ASCII UTF-8
|
||||
characters using the <tt>:string</tt> data type, you need to make sure that
|
||||
Ruby is using UTF-8 as the default encoding for its strings.
|
||||
|
||||
The default in Ruby to use US-ASCII encoding for strings, which causes a string
|
||||
validation check in the Ruby SOAP library to fail and your string to be sent
|
||||
back as a Base-64 value, which may confuse clients that expected strings
|
||||
because of the WSDL.
|
||||
|
||||
Two ways of doing setting the default string encoding are:
|
||||
|
||||
* Start Ruby using the <tt>-Ku</tt> command-line option to the Ruby executable
|
||||
* Set the <tt>$KCODE</tt> flag in <tt>config/environment.rb</tt> to the
|
||||
string <tt>'UTF8'</tt>
|
||||
|
||||
|
||||
== Testing your APIs
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
|
||||
class SoapProtocol < AbstractProtocol # :nodoc:
|
||||
DefaultEncoding = 'utf-8'
|
||||
AWSEncoding = 'UTF-8'
|
||||
XSDEncoding = 'UTF8'
|
||||
|
||||
attr :marshaler
|
||||
|
||||
|
@ -27,10 +28,9 @@ module ActionWebService # :nodoc:
|
|||
def decode_action_pack_request(action_pack_request)
|
||||
return nil unless soap_action = has_valid_soap_action?(action_pack_request)
|
||||
service_name = action_pack_request.parameters['action']
|
||||
charset = parse_charset(action_pack_request.env['HTTP_CONTENT_TYPE'])
|
||||
protocol_options = {
|
||||
:soap_action => soap_action,
|
||||
:charset => charset
|
||||
:charset => AWSEncoding
|
||||
}
|
||||
decode_request(action_pack_request.raw_post, service_name, protocol_options)
|
||||
end
|
||||
|
@ -42,8 +42,7 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
|
||||
def decode_request(raw_request, service_name, protocol_options={})
|
||||
charset = protocol_options[:charset] || DefaultEncoding
|
||||
envelope = SOAP::Processor.unmarshal(raw_request, :charset => charset)
|
||||
envelope = SOAP::Processor.unmarshal(raw_request, :charset => AWSEncoding)
|
||||
unless envelope
|
||||
raise ProtocolError, "Failed to parse SOAP request message"
|
||||
end
|
||||
|
@ -109,8 +108,17 @@ module ActionWebService # :nodoc:
|
|||
end
|
||||
end
|
||||
envelope = create_soap_envelope(response)
|
||||
charset = protocol_options[:charset] || DefaultEncoding
|
||||
Response.new(SOAP::Processor.marshal(envelope, :charset => charset), "text/xml; charset=#{charset}", return_value)
|
||||
|
||||
# FIXME: This is not thread-safe, but StringFactory_ in SOAP4R only
|
||||
# reads target encoding from the XSD::Charset.encoding variable.
|
||||
# This is required to ensure $KCODE strings are converted
|
||||
# correctly to UTF-8 for any values of $KCODE.
|
||||
previous_encoding = XSD::Charset.encoding
|
||||
XSD::Charset.encoding = XSDEncoding
|
||||
response_body = SOAP::Processor.marshal(envelope, :charset => AWSEncoding)
|
||||
XSD::Charset.encoding = previous_encoding
|
||||
|
||||
Response.new(response_body, "text/xml; charset=#{AWSEncoding}", return_value)
|
||||
end
|
||||
|
||||
def protocol_client(api, protocol_name, endpoint_uri, options={})
|
||||
|
@ -138,15 +146,6 @@ module ActionWebService # :nodoc:
|
|||
soap_action
|
||||
end
|
||||
|
||||
def parse_charset(content_type)
|
||||
return DefaultEncoding if content_type.nil?
|
||||
if /^text\/xml(?:\s*;\s*charset=([^"]+|"[^"]+"))$/i =~ content_type
|
||||
$1
|
||||
else
|
||||
DefaultEncoding
|
||||
end
|
||||
end
|
||||
|
||||
def create_soap_envelope(body)
|
||||
header = SOAP::SOAPHeader.new
|
||||
body = SOAP::SOAPBody.new(body)
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'stringio'
|
|||
class ActionController::Base; def rescue_action(e) raise e end; end
|
||||
|
||||
module DispatcherTest
|
||||
Utf8String = "One World Caf\303\251"
|
||||
WsdlNamespace = 'http://rubyonrails.com/some/namespace'
|
||||
|
||||
class Node < ActiveRecord::Base
|
||||
|
@ -58,6 +59,7 @@ module DispatcherTest
|
|||
api_method :hash_struct_return, :returns => [[Person]]
|
||||
api_method :thrower
|
||||
api_method :void
|
||||
api_method :test_utf8, :returns => [:string]
|
||||
api_method :hex, :expects => [:base64], :returns => [:string]
|
||||
api_method :unhex, :expects => [:string], :returns => [:base64]
|
||||
end
|
||||
|
@ -223,6 +225,10 @@ module DispatcherTest
|
|||
@void_called = @method_params
|
||||
end
|
||||
|
||||
def test_utf8
|
||||
Utf8String
|
||||
end
|
||||
|
||||
def hex(s)
|
||||
return s.unpack("H*")[0]
|
||||
end
|
||||
|
@ -398,12 +404,6 @@ module DispatcherCommonTests
|
|||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def update_request(ap_request)
|
||||
end
|
||||
|
||||
def check_response(ap_response)
|
||||
end
|
||||
|
||||
def protocol
|
||||
@protocol
|
||||
end
|
||||
|
@ -453,11 +453,10 @@ module DispatcherCommonTests
|
|||
# puts body
|
||||
ap_request = protocol.encode_action_pack_request(service_name, public_method_name, body, :request_class => ActionController::TestRequest)
|
||||
ap_request.env.update(request_env)
|
||||
update_request(ap_request)
|
||||
ap_response = ActionController::TestResponse.new
|
||||
container.process(ap_request, ap_response)
|
||||
# puts ap_response.body
|
||||
check_response(ap_response)
|
||||
@response_body = ap_response.body
|
||||
public_method_name, return_value = protocol.decode_response(ap_response.body)
|
||||
unless is_exception?(return_value) || virtual
|
||||
return_value = method.cast_returns(return_value)
|
||||
|
|
|
@ -67,16 +67,23 @@ class TC_DispatcherActionControllerSoap < Test::Unit::TestCase
|
|||
assert_equal(["bloggerCat1", "bloggerCat2"], blogger_cats)
|
||||
end
|
||||
|
||||
def test_utf8
|
||||
@direct_controller.web_service_exception_reporting = true
|
||||
$KCODE = 'u'
|
||||
assert_equal(Utf8String, do_method_call(@direct_controller, 'TestUtf8'))
|
||||
|
||||
# If $KCODE is not set to UTF-8, any strings with non-ASCII UTF-8 data
|
||||
# will be sent back as base64 by SOAP4R. By the time we get it here though,
|
||||
# it will be decoded back into a string. So lets read the base64 value
|
||||
# from the message body directly.
|
||||
$KCODE = 'NONE'
|
||||
do_method_call(@direct_controller, 'TestUtf8')
|
||||
retval = SOAP::Processor.unmarshal(@response_body).body.response
|
||||
assert retval.is_a?(SOAP::SOAPBase64)
|
||||
assert_equal "T25lIFdvcmxkIENhZsOp", retval.data.to_s
|
||||
end
|
||||
|
||||
protected
|
||||
def update_request(ap_request)
|
||||
ap_request.env.update('HTTP_CONTENT_TYPE' => 'text/xml; charset=us-ascii')
|
||||
end
|
||||
|
||||
def check_response(ap_response)
|
||||
assert_equal 'text/xml; charset=us-ascii', ap_response.headers['Content-Type']
|
||||
assert_match /xml.*?encoding="us-ascii"/, ap_response.body
|
||||
end
|
||||
|
||||
def exception_message(soap_fault_exception)
|
||||
soap_fault_exception.detail.cause.message
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue