WebReference.com - Part 3 of Chapter 3: Professional XML Web Services, from Wrox Press Ltd (3/5)
Professional XML Web Services
SOAP and RPC
We have spent much of this chapter trying to show that SOAP does not necessarily have to be used for remote procedure calls. The truth is that RPC is what gets most developers excited about SOAP, and with good reason. The idea that the complexities of CORBA and DCOM can be forgotten with a little XML is an attractive concept. The SOAP specification clearly describes how remote procedure calls should be represented in SOAP messages.
Saying that SOAP replaces CORBA or DCOM is an oversimplification, however. SOAP is missing most of the features that developers expect from a robust distributed object protocol, such as garbage collection or object pooling. Despite the letter "O" in SOAP, as the Specification stands today, there are no "objects" in the DCOM or CORBA sense. The SOAP Specification makes it clear that this was not a design goal of the authors. On the other hand, it is clear that SOAP was designed with RPC in mind.
SOAP RPC Convention
We already discussed the open nature of the SOAP specification as it relates to message transport. SOAP defines a message syntax and exchange model, but it does not try to define the "how" of message exchange. The convention is the set of rules applied to a particular use of SOAP messages. The Specification defines rules for a single convention: remote procedure calls, or RPC. The RPC convention can be defined as a way of serializing remote procedure calls and responses as SOAP messages. At the end of this chapter we will discuss the implications of using some alternative conventions.
Like its partner HTTP, SOAP RPC uses a request-response model for message exchanges. Making a remote procedure call with SOAP just involves building a SOAP message. The request SOAP message that is sent to the endpoint represents the call, and the response SOAP message represents the results of that call. Let's take a look at the rules for building method calls and returns in SOAP messages.
Making a remote procedure call with SOAP just involves building a SOAP message. The message that is sent to the endpoint represents the call. The payload of that request message contains a struct that is the serialized method call. The child elements of that struct are the inbound parameters of the method. The end result is an XML representation of a call that looks the way we would expect.
When RPC calls are serialized in a SOAP message, the name of the element must match the name of the method, as do the parameters. Consider this method signature:
// Return the current stock price, given the company symbol double GetStockQuote ( [in] string sSymbol );
If our method namespace is
http://www.wroxstox.com/, then the serialized method call that requests the stock quote using symbol
OU812 would look like this:
<q:GetStockQuote xmlns:q="http://www.wroxstox.com/"> <q:sSymbol xsi:type="xsd:string">OU812</q:sSymbol> </q:GetStockQuote>
The method name matches the element, as does the parameter. While the parameter names match the child elements, only inbound parameters appear in the serialized call. To illustrate this, let's look at three very similar methods signatures:
// Reverse the string, s, and return the new string. string ReverseString ( [in] string s ); // Reverse the string, s, and return the new string. void ReverseString ( [in] string s, [out] string sRev ); // Reverse the string, s, passed in by reference. void ReverseString ( [in, out] string s );
All three methods would be represented by the same serialized call. If we called the
ReverseString() method with the parameter
s containing a value of
ROHT, the call would be represented as shown (below):
<x:ReverseString xmlns:x="http://www.wrox.com/"> <s xsi:type="xsd:string">ROHT</s> </x:ReverseString>
Now we'll take a look at how the return from a remote procedure call is serialized, and also
how the returns from the different forms of the
ReverseString() method shown here, would look.
As we mentioned earlier, the RPC convention uses a request-response model. Just as the call is represented in the request SOAP message, the results of the call are returned in the response SOAP message. The payload in the response also contains a struct, and the child elements are the outbound parameters and/or the return value of the method.
The name of the method response struct can be anything, but it is a convention to append
the word "Response" to the name of the method call struct. For example,
ReverseStringResponse. Just as in the call, the names of parameter elements are
significant and should match the parameters. If the method returns a value, the name is irrelevant, but it must be the first child of the method struct.
So how do we know the difference between a single out parameter and a return value? In one sense, there is no difference. Both are returning a single value as part of the method return. The real answer lies in the name of the parameter. If the name of the parameter matches a parameter of the method, then it is an out parameter. Return values cannot be identified by name, only position, but the name should not conflict with the parameters of the method.
Now that we know how a serialized return should look, let's continue to use our
ReverseString example. As we saw before, all three method signatures for
ReverseString result in the same serialized call. However, how do the serialized returns differ? The first version reverses the string and returns the result as the return value of the method:
<x:ReverseStringResponse xmlns:x="http://www.wrox.com/"> <x:ret xsi:type="xsd:string">THOR</x:ret> </x:ReverseString>
The second version has no return value, but instead uses an out parameter called
<x:ReverseStringResponse xmlns:x="http://www.wrox.com/"> <x:sRev xsi:type="xsd:string">THOR</x:sRev> </x:ReverseString>
The final version reverses the string after passing it by reference, so the parameter
s is both an in and out parameter:
<x:ReverseStringResponse xmlns:x="http://www.wrox.com/"> <x:s xsi:type="xsd:string">THOR</x:s> </x:ReverseString>
As can be seen, the difference between values returned in parameters and the value of
the method itself is all in the name. In the first case, the element was named
in the second case, it was named s. We can access parameters by name or position as elements
of the RPC struct, but the return value can only be accessed by position.
What if there are no out parameters or return values? In that case, we still have a response that represents a method return, but with no data:
Now that we know how to work with remote procedure calls, let's look at a more complete example that uses full messages and a transport.
Created: November 26, 2001
Revised: November 26, 2001