如何使用 Savon 进行包含命名空间的 SOAP 调用

How to make a SOAP call including namespace with Savon

我正在尝试连接到 this SOAP API. Specifically, I am trying to login via this login call

文档指出我应该提出以下请求:

POST /service/replicatorV4.asmx HTTP/1.1
Host: demo12231.srv106.webshopdemo.net
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/Login"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <Login xmlns="http://tempuri.org/">
      <username>string</username>
      <password>string</password>
    </Login>
  </soap:Body>
</soap:Envelope>

当我在SoapUI 发出以下请求:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
  <soapenv:Header/>
  <soapenv:Body>
    <tem:Login>
      <tem:username>Administrator</tem:username>
      <tem:password>passw0rd12</tem:password>
    </tem:Login>
  </soapenv:Body>
</soapenv:Envelope>

然后我得到了这个令人满意的回复:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <LoginResponse xmlns="http://tempuri.org/">
      <LoginResult>48594fe6-41fd-45f9-9f84-89ef8c247b85</LoginResult>
    </LoginResponse>
  </soap:Body>
</soap:Envelope>

但是,当我和 Savon 提出这样的请求时:

require 'savon'

client          = Savon.client( :wsdl => "http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx?WSDL",
                                :open_timeout => 100,
                                :read_timeout => 100,
                                :ssl_verify_mode => :none,
                                :log_level => :debug,
                                :log => false,
                                :logger => Rails.logger,
                                :convert_request_keys_to => :camelcase)

response        = client.call(:login, :message => {:username => "Administrator", :password => "passw0rd12"})

然后我得到了这个不满意的回复:

#<Savon::Response:0x007ff78cfcc368
 @globals=
  #<Savon::GlobalOptions:0x007ff782fb8160
   @option_type=:global,
   @options=
    {:encoding=>"UTF-8",
     :soap_version=>1,
     :namespaces=>{},
     :logger=>
      #<ActiveSupport::Logger:0x007ff78b136ea8
       @default_formatter=
        #<Logger::Formatter:0x007ff78b136e30 @datetime_format=nil>,
       @formatter=
        #<ActiveSupport::Logger::SimpleFormatter:0x007ff783e99f80
         @datetime_format=nil>,
       @level=0,
       @logdev=
        #<Logger::LogDevice:0x007ff78b136de0
         @dev=
          #<File:/Users/me/Projects/my_project/log/development.log>,
         @filename=nil,
         @mutex=
          #<Logger::LogDevice::LogDeviceMutex:0x007ff78b136db8
           @mon_count=0,
           @mon_mutex=#<Mutex:0x007ff78b136d68>,
           @mon_owner=nil>,
         @shift_age=nil,
         @shift_size=nil>,
       @progname=nil>,
     :log=>false,
     :filters=>[],
     :pretty_print_xml=>false,
     :raise_errors=>true,
     :strip_namespaces=>true,
     :convert_response_tags_to=>
      #<Proc:0x007ff782fc3e70@/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/savon-2.11.1/lib/savon/options.rb:85 (lambda)>,
     :convert_attributes_to=>
      #<Proc:0x007ff782fc3df8@/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/savon-2.11.1/lib/savon/options.rb:86 (lambda)>,
     :multipart=>false,
     :adapter=>nil,
     :use_wsa_headers=>false,
     :no_message_tag=>false,
     :follow_redirects=>false,
     :unwrap=>false,
     :host=>nil,
     :wsdl=>
      "http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx?WSDL",
     :open_timeout=>100,
     :read_timeout=>100,
     :ssl_verify_mode=>:none,
     :convert_request_keys_to=>:camelcase,
     :endpoint=>
      #<URI::HTTP:0x007ff78479cbf0 URL:http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx>}>,
 @http=
  #<HTTPI::Response:0x007ff78cfcd2b8
   @body=
    "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><LoginResponse xmlns=\"http://tempuri.org/\"><LoginResult xsi:nil=\"true\" /></LoginResponse></soap:Body></soap:Envelope>",
   @code=200,
   @headers=
    {"Cache-Control"=>"private, max-age=0",
     "Content-Type"=>"text/xml; charset=utf-8",
     "Server"=>"Microsoft-IIS/8.5",
     "Date"=>"Thu, 27 Aug 2015 05:29:42 GMT",
     "Content-Length"=>"335"},
   @raw_body=
    "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><LoginResponse xmlns=\"http://tempuri.org/\"><LoginResult xsi:nil=\"true\" /></LoginResponse></soap:Body></soap:Envelope>">,
 @locals=
  #<Savon::LocalOptions:0x007ff78ce7f550
   @option_type=:local,
   @options=
    {:advanced_typecasting=>true,
     :response_parser=>:nokogiri,
     :multipart=>false,
     :message=>{:username=>"Administrator", :password=>"passw0rd12"},
     :soap_action=>"http://tempuri.org/Login"}>>

响应的正文部分是:

{:login_response=>{:login_result=>nil, :@xmlns=>"http://tempuri.org/"}}

这告诉我,也许服务确实收到了整体请求,但没有获得变量。响应看起来很像我在 SoapUI 中使用错误的 usernamepassword:

发出此请求时的响应
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <LoginResponse xmlns="http://tempuri.org/">
      <LoginResult xsi:nil="true"/>
    </LoginResponse>
  </soap:Body>
</soap:Envelope>

我注意到变量中需要命名空间 tem。这可能与我的问题有关吗?还是有别的解释?

创建一个没有 Rails 的纯 Ruby 脚本。

然后根据您使用 SoapUI 创建的内容检查输出。 Post 差异,有人会提供帮助。

当我 运行 这个脚本时:

require 'savon'

c = Savon.client(wsdl:
                "http://demo12231.srv106.webshopdemo.net/service/replicatorV4.asmx?WSDL",
                log_level: :debug,
                log: true,
                pretty_print_xml: true)
response = c.call(:login,
                  :message => {:username => "Administrator", :password => "passw0rd12"})

对我有用。它可能是你的 Rails 部分中的某些东西会干扰。我不这样做 Rails 因此我在那里帮不了什么忙(我更喜欢 Sinatra :-))。