Python - Zeep SOAP Complex Header
Asked Answered
S

4

7

I'd like to pass "Complex" Header to a SOAP service using the zeep library.

Here's what it should look like

<soapenv:Header>
    <something:myVar1>FOO</something:myVar1>
    <something:myVar2>JAM</something:myVar2>
</soapenv:Header>

I guess that I succeed in sending a header this way

header = xsd.Element(
    '{http://urlofthews}Header',
        xsd.ComplexType([
        xsd.Element(
        '{http://urlofthews}myVar1',
        xsd.String()),
        xsd.Element(
        '{http://urlofthews}myVar2',
        xsd.String())
        ])
    )

header_value = header(myVar1='FOO',myVar2='JAM')
print (header_value)
   
datasoap=client.service.UserRessourcesCatalog(requete,_soapheaders=[header_value])

But how can I declare and pass the namespace "something" in my Header with the XSD?

As mentionned in the documentation:
http://docs.python-zeep.org/en/master/headers.html

Another option is to pass an lxml Element object. This is generally useful if the wsdl doesn’t define a soap header but the server does expect it

...which is my case, so I tried:

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

ET.register_namespace('something', 'http://urlofthews')

headerXML = ET.Element("soapenv:Header")
var1 = ET.SubElement(headerXML, "something:myVar1")
var1.text = "FOO"
var2 = ET.SubElement(headerXML, "something:myVar2")
var2.text = "JAM"


headerDict=xmltodict.parse(ET.tostring(headerXML))
print (json.dumps(headerDict))
       
datasoap=client.service.UserRessourcesCatalog(requete,_soapheaders=headerDict)

But I get:

ComplexType() got an unexpected keyword argument u'soapenv:Header'. Signature: ``

Sideshow answered 22/3, 2017 at 21:37 Comment(0)
P
31

I recently encountered this problem, and here is how I solved it.

Say you have a 'Security' header that looks as follows...

<env:Header>
<Security>
    <UsernameToken>
        <Username>__USERNAME__</Username>
        <Password>__PWD__</Password>
    </UsernameToken>
    <ServiceAccessToken>        
        <AccessLicenseNumber>__KEY__</AccessLicenseNumber>
    </ServiceAccessToken>
</Security>
</env:Header>

In order to send this header in the zeep client's request, you would need to do the following:

header = zeep.xsd.Element(
            'Security',
            zeep.xsd.ComplexType([
                zeep.xsd.Element(
                    'UsernameToken',
                    zeep.xsd.ComplexType([
                        zeep.xsd.Element('Username',zeep.xsd.String()),
                        zeep.xsd.Element('Password',zeep.xsd.String()),
                    ])
                ),
                zeep.xsd.Element(
                    'ServiceAccessToken',
                    zeep.xsd.ComplexType([
                        zeep.xsd.Element('AccessLicenseNumber',zeep.xsd.String()),
                    ])
                ),
            ])
        )

header_value = header(UsernameToken={'Username':'test_user','Password':'testing'},UPSServiceAccessToken={'AccessLicenseNumber':'test_pwd'})

client.service.method_name_goes_here(
                    _soapheaders=[header_value],#other method data goes here
                )
Physiognomy answered 28/6, 2017 at 15:11 Comment(5)
Thx for your explanation OblivionSideshow
You are welcome. Since it is still marked as a 'zeep' question, is there any chance you were able to confirm my answer works - so you can accept it?Physiognomy
This solution worked really well for me. Thank you very much, there are no examples about complex headers for soap requests using Zeep.Petronille
Glad it helped you out SergioMP.Physiognomy
How would this be written if the spec did not contain the enveloping 'Security' element and 'env:Header' just retained the 'UsernameToken' and 'ServiceAccessToken'? Is it just as simple as removing the outside zeep.xsd.Element? I have a similar question hereDespondent
S
2

Thx Oblivion02.

I finally use a raw method

headers = {'content-type': 'text/xml'}
body = """<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://blablabla">
<soapenv:Header>
<something:myVar1>FOO</something:myVar1>
<something:myVar2>JAM</something:myVar2>
</soapenv:Header>
<soapenv:Body>
          ...
</soapenv:Body>
</soapenv:Envelope>"""

response = requests.post(wsdl,data=body,headers=headers)
Sideshow answered 6/7, 2017 at 21:30 Comment(1)
Do you think you could accept my answer? The question is still tagged as a zeep question - so while your answer works using the raw method - the answer I provided uses the zeep library.Physiognomy
U
0

It's an old question, but there are lots of related issues for setting soap headers with zeep. The docs says there is four ways to do it, but does not give example for the last one, so here is one.

It is better than the raw call because you still use zeep for the body and soap itself.

It is simpler than declaring the complete xsd structure.

from zeep import Client
from lxml import etree

headers = etree.XML('<Header><Token xmlns="http://www.example.com/objets/Authentification/1.0">'+result["Token"]+'</Token></Header>')

client = Client("https://www.example.com/services/ServiceName/4.2/Service.svc?singleWsdl")

result = client.service.MethodeName(
        payload,
        _soapheaders=[*headers],
    )
Unplug answered 20/8 at 13:7 Comment(0)
E
-1

From a related question:
Bearer Token authorization header in SOAP client with Zeep and Python

import requests
from zeep import Client, Transport

headers = {
    "Authorization": "Bearer " + get_token()
}
session = requests.Session()
session.headers.update(headers)
transport = Transport(session=session)
client = Client(wsdl=url, transport=transport)
Ejector answered 10/7 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.