I recently spent time spelunking some corners of ASMX's support for WSDL and SOAP. I sent around my findings internally and several people mentioned that they may be of broader interest.
Note that everything here assumes doc/lit and where relevant, SoapParameterStyle.Bare.
In general, you have to use the wsdl:part/@element attribute with doc/lit EXCEPT when using ASMX's “wildcard mode.” Wildcard mode uses the wsdl:part/@type attribute and places no restriction on the type of the body element(s). In wildcard mode, ASMX won't try to deserialize into a strongly typed object but rather presents the body as one or more XmlElement references.
The "code-first" way to get ASMX to use wildcard mode is to mark an operation as SoapParameterStyle.Bare and use a single parameter (or return value) of type XmlElement or XmlElement[] that is marked as [XmlAnyElement] like this:
[SoapDocumentMethod(ParameterStyle=SoapParamterStyle.Bare, OneWay=true)] public void DoIt([XmlAnyElement] XmlElement[] bodies) { … }
If your parameter is an array, then multiple children of S:Body are supported. If your parameter is a single XmlElement, then only the first child element of S:Body will be used (any additional child elements are dropped on the floor at deserialization-time).
The "contract-first" way to use wildcard mode is to write a wsdl:input or wsdl:output stage with exactly one message part that uses the wsdl:part/@type attribute that points to a xs:complexType that follows the following rules:
The complex type can contain at most one particle in its content model and that particle must be an an element wildcard (xsd:any).
There are no restrictions on the values of minOccurs/maxOccurs on the element wildcard, however, the values influence the generated code at import-time. If maxOccurs > 1, the parameter shows up as an XmlElement[]. If maxOccurs = 1, the parameter shows up as an XmlElement.
Finally, there’s a quirk in the WSDL generation when one defines a wildcard message in an ASMX service. Specifically, the complexType is marked mixed=”true” which is wrong (you cannot have mixed content under S:Body).
There is a way to programmatically define an ASMX service or proxy to collect all headers by defining a field of type SoapUnknownHeader[] and marking your methods with the [SoapHeader] attribute that names the field.
Wildcard mode relies on WSDL constructs that aren't WS-I BP 1.0 compliant. However, if you restrict yourself to single-element bodies (e.g., just use XmlElement, not XmlElement[]), your messages can still be BP compliant even if your descriptions aren't.
When not in wildcard mode (that is, when you're using the wsdl:part/@element), ASMX will try to deserialize each child element of S:Header and S:Body into a field (for headers) or parameter/return value (for bodies).
ASMX ignores the order of message parts at deserialization time. Specifically, body parts A and B can appear in either order on incoming messages.
Use of soapbind:header or soapbind:body implies that parts are OPTIONAL, not mandatory. Null references are translated into missing elements and vice-versa.
ASMX doesn't handle child elements of S:Header or S:Body with duplicate QNames well.
ASMX cannot import a WSDL binding that has two or more soapbind:body elements (or two or more soapbind:header elements) that reference the same global element declaration. ASMX can cope with a WSDL binding that specifies the same QName for exactly one soapbind:header and soapbind:body element however. Moreover, ASMX cannot define a method that results in such a binding. If you have two parameters (or two [SoapHeader] fields) that would result in the same QName, ASMX will produce an error.
If at deserialization-time more than one child of S:Header or S:Body matches a given field/parameter, ASMX will deserialize the first element in the message that matches. Additional matching header elements will go into the SoapUnknownHeader array if present. Additional matching body blocks are silently dropped on the floor.
The net-net of this is that pragmatically ASMX treats the soapbind:header/soapbind:body like an XSD all compositor that uses minOccurs=0 on each particle and has an extra xsd:any interleaved between each particle (which technically can't be expressed in XSD or even RNG).
For example, the following WSDL:
<wsdl:input>
<soap:body parts=”A B C” use=”literal” />
<soap:header message=”tns:MP” part=”D” use=”literal” />
<soap:header message=”tns:MP” part=”E” use=”literal” />
<soap:header message=”tns:MP” part=”F” use=”literal” />
</wsdl:input>
Is equivalent to the following XSD (modulo the lack of interleaved wildcards):
<xs:element name=”Envelope”>
<xs:complexType>
<xs:sequence>
<xs:element name=”Header” minOccurs=”0”>
<xs:complexType>
<xs:all>
<xs:element ref=”tns:D” minOccurs=”0” />
<xs:element ref=”tns:E” minOccurs=”0” />
<xs:element ref=”tns:F” minOccurs=”0” />
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name=”Body” >
<xs:complexType>
<xs:all>
<xs:element ref=”tns:A” minOccurs=”0” />
<xs:element ref=”tns:B” minOccurs=”0” />
<xs:element ref=”tns:C” minOccurs=”0” />
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
Posted
Nov 21 2004, 09:32 PM
by
don-box