RPC/Literal and Freedom of Choice

 

Yasser Shohoud

April 2003

Applies to:

   Remote Procedure Calls (RPC)
   SOAP Messaging
   SOAP 1.1 Specification
   WSDL 1.1 Specification
   WS-I Basic Specification

Summary: See why the SOAP messaging format you use for your Web service doesn't necessarily determine the programming model you use. Learn more about SOAP messaging formats and decoupling the message format from the programming model. (11 printed pages)

Contents

SOAP Message Formats
Describing SOAP Message Formats
Converting RPC/literal to Document/literal
Do We Really Need Two Message Formats?
WS-I Basic Profile and RPC/literal

There are two popular programming models for Web services today: RPC and messaging. The WSDL 1.1 specification unnecessarily defines two SOAP message styles, RPC and document, that supposedly correspond to the two programming models. I say unnecessarily because the correlation between programming model and message format is entirely artificial. For example, I can use an RPC programming model to send and receive document-style messages. In fact, Microsoft® .NET-based Web services (.asmx) does exactly that by default. I can also directly send an RPC-style SOAP message using some XML API (e.g. DOM or XmlReader) and then dispatch the received response message to some other routine to process it.

Despite this, there's a strong tendency among tools vendors to infer the programming model from the message format defined in the service's WSDL. I can certainly understand using the message format as a hint for the suggested programming model, but it's a mistake to use it to dictate the programming model. As a Web service consumer, I certainly don't want the service to dictate the programming model I use. Rather, I want the freedom to choose whatever platform, language, and programming model to invoke that service. Tools vendors need to realize that consumer (client) developers are capable of choosing the programming model independent of what service developers recommend.

The rest of this article explains the two SOAP message formats and shows how WSDL describes these formats. It also explains why only one message format is needed and suggests decoupling the programming model from the message format.

SOAP Message Formats

Feel free to skip this section if you're familiar with the four SOAP message formats.

WSDL 1.1 distinguishes between two message styles: document and RPC. Here's how each style affects the contents of <soap:Body>.

  • Document: <soap:Body> contains one or more child elements called parts. There are no SOAP formatting rules for what the <soap:Body> contains; it contains whatever the sender and the receiver agree upon.
  • RPC: RPC implies that <soap:Body> contains an element with the name of the method or remote procedure being invoked. This element in turn contains an element for each parameter of that procedure.

For applications that use serialization/deserialization to abstract away the data wire format, there's one more choice to be made: the serialization format. There are two popular serialization formats today:

  • SOAP Encoding: SOAP encoding is a set of serialization rules defined in section 5 of SOAP 1.1 and is sometimes referred to as "section 5 encoding." The rules specify how objects, structures, arrays, and object graphs should be serialized. Generally speaking, an application using SOAP encoding is focused on remote procedure calls and will likely use RPC message style. The rest of this article ignores SOAP encoding and focuses on literal format.
  • Literal: Data is serialized according to a schema. In practice, this schema is usually expressed using W3C XML Schema. Although there are no prescribed rules for serializing objects, structures, and graphs, etc., the service's schema describes the application-level Infoset of each of the service's messages.

Here are two examples of SOAP messages: document/literal and RPC/literal. Note that both examples happen to (but are not required to) contain an element named Example with a child named cust. However the namespaces of Example and cust elements are different in the two examples, as shown in bold text.

Document/literal

<soap:Envelope 
xmlns:soap="https://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="https://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>
    </x:Example>
  </soap:Body>
</soap:Envelope>

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="https://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:s="http://www.w3.org/2001/XMLSchema" 
xmlns:tns="http://example.org/soapformat" 
xmlns:tm="https://microsoft.com/wsdl/mime/textMatching/" 
targetNamespace="http://example.org/soapformat" 
xmlns="https://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="https://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:body use="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="https://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:s="http://www.w3.org/2001/XMLSchema" 
xmlns:tns="http://example.org/soapformat" 
xmlns:tm="https://microsoft.com/wsdl/mime/textMatching/" 
targetNamespace="http://example.org/soapformat" 
xmlns="https://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="https://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:body use="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

  1. For each part in the input and output messages of that operation, declare a type with the contents described in step 2.
  2. Declare an element with the name of the part, the type referenced by type=". The element form must be unqualified.
  3. 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.
  4. 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.
  5. Change the input message to contain one part and reference the element you declared in step 3.
  6. Change the output message to contain one part and reference the element you declared in step 4.
  7. 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="https://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:body use="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.