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!
You can add soap header information to method calls by decorating the methods in the proxy class generated from the wsdl with the SoapHeader attribute. For example wsdl.exe will generate client proxy class Reference. cs for the web service reference when you "Add Web Reference".
There is only one SOAP header section in a SOAP request. If the SOAP header element is present, it must be the first child element of the envelope element. SOAP headers can be input, output, or input and output, and you do not need to specify them in the WSDL file.
HeaderList hl = (HeaderList) messageContext. get(JAXWSProperties. INBOUND_HEADER_LIST_PROPERTY); which gives you access to all SOAP headers.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With