Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SOAP call in Ruby on Rails using Savon gets weird around the envelope and main operation

In a rails project using Savon.rb I am trying to make a very complex SOAP call. At least complex to the extend that Savon builders are to much trouble so I decided to manipulate the :xml directly.

First of I initiate the client:

@client = Savon.client(
            :endpoint => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/BarcodeWebService.svc',
            :wsdl     => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl')

Then I make the call in the shape of:

@request = @client.build_request(:generate_barcode,
             xml: %Q{ ... see soap call (with ruby interpolation) ... } 

I add a correctly formatted l Time.now, format: :postnl_api string and the rest is still hard coded. Including the message number.

Following is how the call actually is made by Savon in this case, as retrieved using @request.body.

The SOAP call in my application

<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:wsdl="http://tempuri.org/"
  xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode
    </Action>
    <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <UsernameToken>
        <Username>devc_!R4xc8p9</Username>
        <Password>xxxxxxxx</Password>
      </UsernameToken>
    </Security>
  </s:Header>
  <s:Body>
    <wsdl:GenerateBarcode>
      <d6p1:Customer>
        <d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
        <d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
      </d6p1:Customer>
      <d6p1:Barcode>
        <d6p1:Type>3S</d6p1:Type>
        <d6p1:Range>DEVC</d6p1:Range>
        <d6p1:Serie>1000000-2000000</d6p1:Serie>
      </d6p1:Barcode>
    </wsdl:GenerateBarcode>
  </s:Body>
</s:Envelope>

Then the following is how a call should look as I've seen this one having success in a sandbox environment of the company.

The SOAP call as it should be

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode</Action>
    <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <wsse:Username>devc_!R4xc8p9</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxxxxxxx</wsse:Password>
      </wsse:UsernameToken>
    </Security>
  </s:Header>
  <s:Body>
    <GenerateBarcode xmlns:d6p1="http://postnl.nl/cif/domain/BarcodeWebService/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://postnl.nl/cif/services/BarcodeWebService/">
      <d6p1:Message>
        <d6p1:MessageID>5</d6p1:MessageID>
        <d6p1:MessageTimeStamp>28-06-2017 14:15:41</d6p1:MessageTimeStamp>
      </d6p1:Message>
      <d6p1:Customer>
        <d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
        <d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
      </d6p1:Customer>
      <d6p1:Barcode>
        <d6p1:Type>3S</d6p1:Type>
        <d6p1:Range>DEVC</d6p1:Range>
        <d6p1:Serie>1000000-2000000</d6p1:Serie>
      </d6p1:Barcode>
    </GenerateBarcode>
  </s:Body>
</s:Envelope>

The main thing that seems to be off (and this was also the case using the Savon builder btw) is the envelope's attributes and the main operation's :generate_barcode shape as well as its attributes. I don't get why I get the prefix wsdl: before GenerateBarcode.

I tell Savon to take my xml and build it exactly like that, but it does not work. Sending my version as-is returns an Error 400.

EDIT using Chris his part

Using @Chris his answer I was able to make the following call:

Set up call

@client = Savon.client(
                 :endpoint                => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/BarcodeWebService.svc',
                 :wsdl                    => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl',
                 :log                     => true,
                 :wsse_auth               => [ENV['postnl_username'], ENV['postnl_password']],
                 :pretty_print_xml        => true,
                 :convert_request_keys_to => :camelcase,
                 :env_namespace           => :s)

     message =  {
                  "d6p1:Message" => {
                    "d6p1:MessageID" =>  "7",
                    "d6p1:MessageTimeStamp" => I18n.l( Time.now, format: :postnl_api)
                },
                  "d6p1:Customer" => {
                    "d6p1:CustomerCode" => "DEVC",
                    "d6p1:CustomerNumber" =>  "11223344"},
                    "d6p1:Barcode" => {
                      "d6p1:Type" => "3S",
                      "d6p1:Range" => "DEVC",
                      "d6p1:Serie" => "1000000-2000000" } 
                }
     @client.call(:generate_barcode, :message => message, :soap_header => { "Action" => "http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode"})

Call

<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://tempuri.org/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action>http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode</Action>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-1">
        <wsse:Username>devc_!R4xc8p9</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">098fd559930983af31ef6630a0bb0c1974156561</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </s:Header>
  <s:Body>
    <wsdl:GenerateBarcode>
      <d6p1:Message>
        <d6p1:MessageID>7</d6p1:MessageID>
        <d6p1:MessageTimeStamp>17-07-2017 22:13:35</d6p1:MessageTimeStamp>
      </d6p1:Message>
      <d6p1:Customer>
        <d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
        <d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
      </d6p1:Customer>
      <d6p1:Barcode>
        <d6p1:Type>3S</d6p1:Type>
        <d6p1:Range>DEVC</d6p1:Range>
        <d6p1:Serie>1000000-2000000</d6p1:Serie>
      </d6p1:Barcode>
    </wsdl:GenerateBarcode>
  </s:Body>
</s:Envelope>

Response:

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
      <faultstring xml:lang="en-US">The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</faultstring>
    </s:Fault>
  </s:Body>
</s:Envelope>
like image 231
Code-MonKy Avatar asked Jul 07 '17 09:07

Code-MonKy


1 Answers

Ok this works (verified)

@client = Savon.client(
             :wsdl                    => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl',
             :log                     => true,
             :wsse_auth               => ['devc_!R4xc8p9', 'xxx'],
             :pretty_print_xml        => true,
             :convert_request_keys_to => :camelcase,
             :env_namespace           => :s,
             :namespace_identifier    => nil
            )

 message =  {
              "d6p1:Message" => {
                "d6p1:MessageID" =>  "10",
                "d6p1:MessageTimeStamp" => Time.now.strftime("%d-%m-%Y %H:%M:%S")
            },
              "d6p1:Customer" => {
                "d6p1:CustomerCode" => "DEVC",
                "d6p1:CustomerNumber" =>  "11223344"},
                "d6p1:Barcode" => {
                  "d6p1:Type" => "3S",
                  "d6p1:Range" => "DEVC",
                  "d6p1:Serie" => "1000000-2000000" } 
            }


attributes = { "xmlns:d6p1" => "http://postnl.nl/cif/domain/BarcodeWebService/", 
               "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance", 
               "xmlns" => "http://postnl.nl/cif/services/BarcodeWebService/"}

@client.call(:generate_barcode, :attributes => attributes, 
             :message => message, 
             :soap_header => { "Action" => "http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode"})

So the trick was adding :namespace_identifier => nil and sending attributes. Setting namespace_identifier removes the wsdl from GenerateBarcode ans attributes sets some namespaces on GenerateBarcode tag. Now I remember why I hate SOAP so much :(

like image 57
Chris Avatar answered Nov 11 '22 00:11

Chris