kramer65
kramer65

Reputation: 53843

Why can't I set SOAP headers in pysimplesoap?

I received some example php code for calling a SOAP service which I now need to convert to Python. In the php code they set the headers as follows:

$auth = array();
$auth['token'] = 'xxx';
if ($auth) {
    // add auth header
    $this->clients[$module]->__setSoapHeaders(
        new SoapHeader(
            $namespace, 
            'auth', 
            $auth
        )
    );
}

So the auth header should look like this: ['token' => 'xxx']. I then loaded the wsdl into SoapUI, which gave me the following example xml:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sub="https://example.com/path/to/sub">
   <soapenv:Header>
      <sub:auth>
         <token>?</token>
         <!--Optional:-->
         <user_id>?</user_id>
         <!--Optional:-->
         <user_token>?</user_token>
      </sub:auth>
   </soapenv:Header>
   <soapenv:Body>
      <sub:customer_logos_pull>
         <!--Optional:-->
         <language>?</language>
         <!--Optional:-->
         <limit>?</limit>
         <!--Optional:-->
         <options_utc>?</options_utc>
      </sub:customer_logos_pull>
   </soapenv:Body>
</soapenv:Envelope>

In pysimplesoap I now try something like this:

from pysimplesoap.client import SoapClient

WSDL = 'https://example.com/some/path/sub.wsdl'
TOKEN = 'xxx'

client = SoapClient(wsdl=WSDL, trace=True)
client['auth'] = {'token': TOKEN}
print client.customer_logos_pull({})

but I get an error saying ExpatError: not well-formed (invalid token): line 1, column 0, which makes sense, because in the logged xml I see that the header is empty:

<soap:Header/>

I tried varying the code by including the sub: before auth like this: client['sub:auth'] = {'token': TOKEN}, but I get the same error.

Does anybody know what I'm doing wrong here? All tips are welcome!

Upvotes: 6

Views: 932

Answers (1)

James Mills
James Mills

Reputation: 19030

So I think we can solve this by using the suds library.

Here is a very basic example of how to send a SOAP request that includes headers:

Example:

from suds.sax.element import Element 

client = client(url) 
ssnns = ('ssn', 'http://namespaces/sessionid') 
ssn = Element('SessionID', ns=ssnns).setText('123') 
client.set_options(soapheaders=ssn)  
result = client.service.addPerson(person)

This is an example of how you'd send the following header:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP ENC="http://www.w3.org/2003/05/soap-encoding">
    <ssn:SessionID SOAP-ENV:mustUnderstand="true">123</ssn:SessionID>
 </SOAP-ENV:Header>

NB: I haven't actually tried this per se as I don't have access to any readily available SOAP/XML services I can test against!

So in your particular example you would do something like this:

>>> from suds.sax.element import Element
>>> subns = ("sub", "http://namespaces/sub")
>>> sub = Element("auth", ns=subns)
>>> token = Element("token").setText("?")
>>> user_id = Element("user_id").setText("?")
>>> user_token = Element("user_token").setText("?")
>>> sub.append(token)
Element (prefix=sub, name=auth)
>>> sub.append(user_id)
Element (prefix=sub, name=auth)
>>> sub.append(user_token)
Element (prefix=sub, name=auth)
>>> print(sub.str())
<sub:auth xmlns:sub="http://namespaces/sub">
   <token>?</token>
   <user_id>?</user_id>
   <user_token>?</user_token>
</sub:auth>

Then call set_options() on your client object:

client.set_options(soapheaders=sub)

And you can easily install suds using pip by running:

$ pip install suds

Upvotes: 3

Related Questions