xsdata icon indicating copy to clipboard operation
xsdata copied to clipboard

SOAP input/output messages missing `Header` element

Open mmakaay opened this issue 1 year ago • 2 comments

When generating SOAP messages from a WSDL, the Envelope for input and output defines only a Body and no Header.

Envelope specification: https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383494

A SOAP message is an XML document that consists of a mandatory SOAP envelope, an optional SOAP header, and a mandatory SOAP body.

A generated SOAP message now looks like:

@dataclass
class SomeOperationInput:
    class Meta:
        name = "Envelope"
        namespace = "http://schemas.xmlsoap.org/soap/envelope/"

    body: Optional["SomeOperationInput.Body"] = field(
        default=None,
        metadata={
            "name": "Body",
            "type": "Element",
        },
    )

    @dataclass
    class Body:
    -----8<-------

The Header element is missing here.

As a result, I am unable to parse incoming SOAP requests that I send from SOAP UI. The parser complains about the now unexpected Header element.

xsdata.exceptions.ParserError: Unknown property {http://schemas.xmlsoap.org/soap/envelope/}Envelope:{http://schemas.xmlsoap.org/soap/envelope/}Header

with the request message structure looking like:

<soapenv:Envelope
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
      xmlns:v20="http://www.reggefiber.nl/schemas/odfaccess/order/client/v20140102" 
      xmlns:v201="http://www.exmaple.com/soap">
   <soapenv:Header/>
   <soapenv:Body>
      <v20:MyRequest>
         <!-- -----8<----- -->
      </v20:MyRequest>
   </soapenv:Body>
</soapenv:Envelope>

Work-around

I currently work around this issue, by removing the Header from the request body, before passing it on to the xsdata parser. I use the following code for this:

from lxml import etree

def strip_header_from_envelope(request_xml: bytes) -> bytes:
    tree = etree.fromstring(request_xml)
    headers = tree.xpath(
        "/soapenv:Envelope/soapenv:Header",
        namespaces={"soapenv": "http://schemas.xmlsoap.org/soap/envelope/"}
    )
    if headers:
        envelope = headers[0].getparent()
        envelope.remove(headers[0])
    request_xml_without_header = etree.tostring(tree, encoding="utf-8")
    return request_xml_without_header

This is not super efficient, since the XML gets deserialzed and serialzed to handle the removal of the Header from the request XML data.

mmakaay avatar Nov 17 '24 19:11 mmakaay

You can disable failures on unknown properties @mmakaay

https://xsdata.readthedocs.io/en/latest/data_binding/basics/#fail_on_unknown_properties

Shouldn't the wsdl include information about the header then, I am not sure what we should generate there...

tefra avatar Dec 01 '24 06:12 tefra

An optional empty Header would do I think, since that is what SOAP UI seems to find valid.

For my use case, I am fine with disabling failures here. Thanks for the hint, and feel free to close this issue.

mmakaay avatar Jan 10 '25 20:01 mmakaay