<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<!-- the following is an XML document described in the service's
contract using XML Schema. In this case Example may or may not be the
name of a remote procedure being invoked by this message.
Also, cust may or may not be the name of a parameter. We know the
structure of the XML document but we don't know how the service
processes it -->
<Example xmlns="http://example.org/soapformat">
<cust>
<Customer>
<Name>John Doe</Name>
<Id>ABC-1234</Id>
</Customer>
<Customer>
<Name>Jane Doe</Name>
<Id>XYZ-1234</Id>
</Customer>
</cust>
</Example>
</soap:Body>
</soap:Envelope>
RPC/literal
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<!-- Example is the name of the procedure being invoked
cust is a parameter of that procedure.
Note that cust is not namespace-qualified.
The two Customer elements are contents of the cust parameter. In this
case cust can be thought of as an array of Customer items.
Note that Customer is namespace qualified but it's in a different
namespace than Example.
These namespace rules are unique to RPC-style messages and will be
explained in the next section -->
<x:Example xmlns:x="http://example.org/soapformat/Example">
<cust>
<t:Customer xmlns:t="http://example.org/soapformat">
<t:Name>John Doe</t:Name>
<t:Id>ABC-1234</t:Id>
</t:Customer>
<t:Customer>
<t:Name>Jane Doe</t:Name>
<t:Id>XYZ-1234</t:Id>
</t:Customer>
</cust>
Describing SOAP Message Formats
Describing document/literal messages
In
this section I will explain how WSDL describes document/literal and
RPC/literal message formats. The examples and explanations I give take
into account WS-I Basic Profile 1.0 recommendations. I will not cover
SOAP encoding to focus on comparing document/literal and RPC/literal.
The listing below shows a WSDL example describing two document/literal messages. The salient features are:
- Each message contains zero or one part. That part points to a schema element declaration that describes the entire contents of the message body.
- In the SOAP binding, style is "document" and use is "literal".
The
primary feature of document/literal, and its key benefit compared to
RPC/literal, is the use of a schema element declaration to completely
describe the contents of soap:Body. This means you can tell
what the message body Infoset contains just by looking at the schema
and with no need for additional rules. Consequently you could take the
schema describing a document/literal message and use it to validate the
message. You can't do this with RPC/literal.
<definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://example.org/soapformat"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
targetNamespace="http://example.org/soapformat"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<s:schema elementFormDefault="qualified"
targetNamespace="http://example.org/soapformat">
<!-- this element declaration describes the
entire contents of the soap:Body in the request messaage.
This is a feature of document/literal that RPC/literal lacks -->
<s:element name="Example">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="cust"
type="tns:ArrayOfCustomer" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="ArrayOfCustomer">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded"
name="Customer" nillable="true"
type="tns:Customer" />
</s:sequence>
</s:complexType>
<s:complexType name="Customer">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1"
name="Name" type="s:string" />
<s:element minOccurs="0" maxOccurs="1"
name="Id" type="s:string" />
</s:sequence>
</s:complexType>
<!-- Similarly, this element declaration describes the
contents of the soap:Body in the response message.
In this case the response is empty -->
<s:element name="ExampleResponse">
<s:complexType />
</s:element>
</s:schema>
</types>
<message name="ExampleSoapIn">
<!-- using element="" to reference an element declaration -->
<part name="parameters" element="tns:Example" />
</message>
<message name="ExampleSoapOut">
<part name="parameters" element="tns:ExampleResponse" />
</message>
<portType name="testserviceSoap">
<operation name="Example">
<input message="tns:ExampleSoapIn" />
<output message="tns:ExampleSoapOut" />
</operation>
</portType>
<binding name="testserviceSoap" type="tns:testserviceSoap">
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
style="document"/>
<operation name="Example">
<soap:operation
soapAction="http://example.org/soapformat/Example"/>
<input>
<soap:bodyuse="literal"/>
</input>
<output>
<soap:bodyuse="literal"/>
</output>
</operation>
</binding>
</definitions>
Describing RPC/literal messages
The listing below shows a WSDL example describing two RPC/literal messages. The salient features are:
- Each message contains zero or more parts. Each part points to a schema type definition that describes the content of that part.
- In the SOAP binding, the style is "RPC" and the use is "literal".
The
primary feature (if you can call it that) of RPC/literal, and its key
drawback compared to document/literal, is the reliance on magic to know
what soap:Body contains. This means the schema alone does not
tell you what the message body Infoset contains, you must also know the
RPC rules. Therefore, the schema describing an RPC/literal message is not sufficient to validate that message.
The rules for deriving an RPC/literal message from its WSDL description are as follows:
- Soap:Body contains one element which has the name of the WSDL operation and the namespace specified on the soap:body element in the WSDL binding (see the namespace="http://example.org/soapformat/Example" attribute in the listing below).
- Inside
this element, there's an element for each part of the message. The name
of this element is the name of the part. This element is unqualified. - Inside each part element are the contents of that part, as defined by the schema type that the part references in WSDL.
Needless
to say, there's quite a bit of magic that has to happen between the
schema describing an RPC/literal message and the actual message on the
wire. Document/literal eliminates this magic.
<definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://example.org/soapformat"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
targetNamespace="http://example.org/soapformat"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<s:schema elementFormDefault="qualified"
targetNamespace="http://example.org/soapformat">
<!-- there are no global element declarations. There's nothing in the
schema that completely describes the content of soap:Body -->
<s:complexType name="ArrayOfCustomer">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded"
name="Customer" nillable="true"
type="tns:Customer" />
</s:sequence>
</s:complexType>
<s:complexType name="Customer">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1"
name="Name" type="s:string" />
<s:element minOccurs="0" maxOccurs="1"
name="Id" type="s:string" />
</s:sequence>
</s:complexType>
</s:schema>
</types>
<message name="ExampleSoapIn">
<!-- using type="" to reference a type declaration -->
<part name="cust" type="tns:ArrayOfCustomer" />
</message>
<message name="ExampleSoapOut"/>
<portType name="testserviceSoap">
<operation name="Example">
<input message="tns:ExampleSoapIn" />
<output message="tns:ExampleSoapOut" />
</operation>
</portType>
<binding name="testserviceSoap" type="tns:testserviceSoap">
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
style="rpc"/>
<operation name="Example">
<soap:operation
soapAction="http://example.org/soapformat/Example"/>
<input>
<soap:body
namespace="http://example.org/soapformat/Example"
use="literal"/>
</input>
<output>
<soap:bodyuse="literal"/>
</output>
</operation>
</binding>
</definitions>
Converting RPC/literal to Document/literal
RPC/literal
turns out to be a subset of document/literal. This means for any given
RPC/literal WSDL, you can create a completely equivalent
document/literal WSDL that would describe the same wire messages. Here
are the steps for doing this (mostly schema element declarations).
For each RPC/literal operation
- For each part in the input and output messages of that operation, declare a type with the contents described in step 2.
- Declare an element with the name of the part, the type referenced by type=". The element form must be unqualified.
- Declare a global element with the operation name and the namespace from soap:body/@namespace in the binding. Make the type of this element the one defined in step 1 for the input message.
- Declare a global element with the operation name and the word Response appended to it and the namespace from soap:body/@namespace in the binding. Make the type of this element the one defined in step 1 for the output message.
- Change the input message to contain one part and reference the element you declared in step 3.
- Change the output message to contain one part and reference the element you declared in step 4.
- Change the style to document in the binding.
Do We Really Need Two Message Formats?
So
do we really need two message formats and all these contrived rules to
derive a message from its description? Absolutely not—for two simple
reasons:
- Document/literal is a superset of
RPC/literal. For any given RPC/literal WSDL description, an equivalent
document/literal WSDL description can be created such that the wire
messages are identical. All that's needed is some schema manipulation
to formally describe the RPC/literal message. - Message format
and programming model are orthogonal. Requiring two message formats
because there are two programming models is bogus. Furthermore, the
service should not dictate the consumer's programming model.
I
do acknowledge, however, the need for a programming model hint that
allows service designers to indicate that their design is optimized for
RPC or messaging programming models. This should be a simple hint that
can be ignored, if desired, by consumer tools. For example, a global
attribute on the binding or operation would do the trick:
<binding
xmlns:x="http://example.org/descriptionHints"
name="testserviceSoap"
type="tns:testserviceSoap"
<!-- the service developer suggests RPC programming style -->x:hint="rpc">
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
style="document"/>
<operation name="Example">
<soap:operation
soapAction="http://example.org/soapformat/Example"/>
<input>
<soap:body
use="literal"/>
</input>
<output>
<soap:bodyuse="literal"/>
</output>
</operation>
</binding>
As
a case in point, .NET-based Web services expose an RPC programming
model and, by default, use document/literal messages. The hint
Microsoft .NET development tools rely on is the name of the message
part in WSDL. If that name is "parameters," the WSDL consumer assumes
that there's a method named after the part's global element declaration
and that each element within that part is a parameter of the method.
The way Microsoft .NET works is only interesting as an example proving
that the RPC programming model and document/literal messages can
coexist. I'm not suggesting everyone should adopt the "parameters"
hint. Another, more explicit mechanism such as a global attribute would
be preferable, as it more clearly indicates the hint and does not
overload the use of the part name.
WS-I Basic Profile and RPC/literal
Unfortunately,
the WS-I Basic Profile explicitly permits the use of both
document/literal and RPC/literal. Given the above analysis, I believe
having two message formats is unnecessary and ultimately does not help
interoperability. Hopefully, most if not all Web service developers
will ignore RPC/literal and provide the necessary feedback to WS-I to fix this in a future version of the Basic Profile.
</x:Example>
</soap:Body>
</soap:Envelope>
No comments:
Post a Comment