Wednesday, October 15, 2008

Interoperability Gotcha: doc/lit/wrapped

@YaronNaveh

Every wsdl has a "style" and an "encoding". They only set the soap message format on-the-wire so they do not matter for the most developers most of the time. However it is highly recommended to always use style=document and encoding=literal to increase interoperability. For example:


<wsdl:operation name="MyOperation">
  <soap12:operation soapAction="http://tempuri.org/MyOperation" style="document" />
  
    <soap12:body use="literal"/>
...


There is a special recommended style to write doc/lit wsdls: doc/lit/wrapped. In this format each message is named with the operation name suffixed by "request" or "response". The message contains only one part named "parameters" (this name never appears in the soap in doc/lit so it shouldn't matter). There are other requirements - see more details at Anne's blog. For example (pseudo wsdl):



<xs:schema>
  <xs:element name="MyOperationRequest">
     <xs:complexType>
       <xs:sequence>
...
       </xs:sequence>
     </xs:complexType>
   </xs:element>
</xs:schema>

...
<wsdl:message name="MyOperationSoapIn">
   <wsdl:part name="parameters" element="tns:MyOperationRequest">
   </wsdl:part>
</wsdl:message>
...
<wsdl:portType name="MyOperationSoap">
   <wsdl:operation name="MyOperation">
    <wsdl:input message="tns:MyOperationSoapIn"/>
...
  </wsdl:Operation>
<wsdl:PortType />



There are a few advantages to doc/lit/wrapped among them a cleaner model and a more expressive soap message. However this style raises a few interoperability issues:


Axis2 code generation
If you've read Anne's explanation you know that the root element in the message is redundant (since it always has to be used anyway) and so the generated stub looks more complex than it should. Keith explains how to solve this.


WCF DataContractSerializer
WCF has a few flavours of serializers. DataContractSerializer is the recommended one. However this serializer can only be used in doc/lit/wrapped. So If your use svcutil to generate a proxy like this:


SvcUtil.exe /t:code /serializer:DataContractSerializer /s /out:IService.cs /n:*,myNamespace /ixt myService.wsdl


You might get the following error:


Warning: The optional WSDL extension element 'body' from namespace 'http://schem
as.xmlsoap.org/wsdl/soap/' was not handled.
XPath: //wsdl:definitions[@targetNamespace='http://myNamespace']/wsdl:bind
ing[@name='MyBinding']/wsdl:operation[@name='MyOperation']/w
sdl:output[@name='MyOperationResponse']


You will get this error if your wsdl does not comply to doc/lit/wrapped in one or more ways. For example if your part names are not "parameters". The way to solve this is either to change the part names to "parameters" (and fix all other issues) or to not force svcutil to use DataContractSerializer:


SvcUtil.exe /t:code /s /out:IService.cs /n:*,myNamespace /ixt myService.wsdl


In this case WCF will downgrade itself to use the .Net 2.0 XmlSerializer. This serializer is not bad at all and was used in .Net 2.0 applications. It does have a less optimal performance than the new serializer but you can use it in production applications if you need. Dan wrote an interesting comparison of the two serializers.

.Net 2.0 derived types
I won't dive into details with this one. Generally speaking .Net 2.0 might try to serialize as doc/lit/wrapped some wsdls which are not really in this format and so it can fail code generation. One example is when the wrapper element has derived types.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

2 comments:

Ismu the Dev said...

I was returning a DataSet and getting this error.
Warning: The optional WSDL extension element 'body' from namespace 'http://schemas.xmlsoap.org/wsdl/soap12/' was not handled.XPath: //wsdl:definitions[@targetNamespace='http://tempuri.org/']/wsdl:binding[@name='']/wsdl:operation[@name='']/wsdl:input

You suggested to change the Part name to parameter name, but I didn't knew what Part name is. Anyways, I was returning DataSet in one of the method an I had not referenced System.Data.dll.
So adding below argument resolved my problem. /r:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll

Yaron Naveh said...

Ismu - thanks for sharing your solution!