A Busy Developer's Guide to WSDL 1.1, Part III
By Sam Ruby, December 17, 2002.
Part I and Part II of this document described how to document first untyped, then typed remote procedure calls using WSDL. This third part documents another style of interaction - exchange of well formed and valid documents. For further insight into this perspective about how one should develop distributed applications, see Sean McGrapth's API's Considered Harmful essay.
SOAP doesn't pick sides in the API vs document debate. If you want to do simple RPC, then peace be with you. If you want to simply exchange documents, then peace be with you too.
The purpose of this document (i.e, Part III) is to describe how WSDL can be used to describe the latter types of exchanges.
Like Part I of this document, the style will be one of progressive disclosure. However, in this case, we will start "from the other end" by describing the service and working our way towards the document description. This not only provides a little variety, it allows the discussion to start with the concepts that are familiar and then work towards the parts of the description where the concepts are different than those found in RPC encoded interfaces.
The example used throughout this document is based closely on the RESTLog interface which creates a new news item. There are two differences that will be introduced in the process: the RSS items will be placed into a proper namespace, and the document itself will be placed inside a SOAP envelope in accordance with the SOAP 1.1 specifications.
There is no difference at the service level, so people who have read Part I will find this part very familiar.
<service name="RESTLog"> <port name="RESTLogSoap" binding="restlog:soap"> <soap:address location="http://localhost/RESTLog.asmx" /> </port> </service>
I prototyped the server side using ASP.Net, and the XML above soap:address location reflects that. Later we will see how this information can be changed dynamically at runtime to point to the resource.
Here we will see the first substantive differences.
<binding name="soap" type="restlog:RESTLogSoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> <operation name="createNews"> <soap:operation soapAction="" style="document" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding></definitions>
Instead of rpc
, you will see
document
. Instead of encoded
, you
will see literal
. This reflects the difference
in usage. It is worth mentioning at this point that this
essentially is a difference in perspective or how you chose to
document an interface. As all RPC calls in SOAP are expressed
in terms of XML, it is generally possible to document the literal
XML directly. The reverse is not always true, as XML elements
documents can have attributes.
A document exchange can be loosely defined as "if I send you a document that looks like this, I can either expect to receive a document that looks that, or a fault." This portion of the WSDL simply defines the two messages.
<message name="createNewsIn"> <part name="item" element="rss:item" /> </message> <message name="createNewsOut"> <part name="createNewsResult" element="restlog:createNewsResult" /> </message> <portType name="RESTLogSoap"> <operation name="createNews"> <input message="restlog:createNewsIn" /> <output message="restlog:createNewsOut" /> </operation> </portType>
In RPC style descriptions of Web Services, the message element plays a much greater role, as a message can contain many typed parts, each corresponding to a parameter. In document style descriptions of Web Services, a message typically is described as a single XML element.
This is the part that can almost be thought of as an afterthought when defining an RPC style service, but is the core of a document literal definition. Since this essay is for educational purposes, I'm not going to document the full Dublin Core or RSS specs in XML Schema here, just enough to get the job done.
<types> <xsd:schema targetNamespace="http://purl.org/dc/elements/1.1/"> <xsd:element name="date" type="xsd:dateTime"/> </xsd:schema> <xsd:schema targetNamespace="http://backend.userland.com/rss2"> <xsd:element name="item" type="rss:Item" /> <xsd:complexType name="Item"> <xsd:sequence> <xsd:element name="title" type="xsd:string" /> <xsd:element name="description" type="xsd:string" /> <xsd:element name="link" type="xsd:anyURI" /> <xsd:element ref="dc:date" /> </xsd:sequence> </xsd:complexType> </xsd:schema> <xsd:schema targetNamespace="http://wellformedweb.org/RESTLog.cgi/5"> <xsd:element name="createNewsResult" type="xsd:int" /> </xsd:schema> </types>
As you can see, the first schema is extremely simple. It defines a single element, named date.
In the second shema, the element is defined in terms of a type. Don't let the name "complexType" fool you, in this case all that is being defined a simple sequence of elements. I could have made this a repeating choice, or gotten really fancy, but I'm intentionally keeping this simple. Also note that a reference is made to the date element from the Dublin Core namespace.
I've also defined a schema for the returned value. This part I've entirely made up as it was not clear to me at least from the existing RESTLog documentation what a successful request will return. Needless to say, such documentation of not only the successful results but also what can be expected upon failure is important.
Accessing this Web Service from .NET
The first thing to do is to use the wsdl.exe tool to produce a proxy class from this WSDL. Compile the generated C# code along with the following test program:
public class test { public static void Main(string[] args) { RESTLog wfw = new RESTLog(); wfw.Url = "http://www.wellformedweb.org/"; Item item = new Item(); item.title="My Summer Vacation"; item.date=System.DateTime.Now; item.link="http://127.0.0.1/cgi-bin/RESTLog.py/4"; item.description = @" On my summer vacation I went to... <a href=""http://www.myrtlebeachonline.com/mld/myrtlebeachonline/""> Myrtle Beach </a>. <br>I got a <b><i>really</b></i> nasty sunburn. "; int itemID = wfw.createNews(item); } }
Now, a few comments on this program. First, note that there is no hint or mention of XML to be found. I create a RESTLog. I create an Item. I fill in a few values. Then I instruct the RESTLog to createNews with the item. The names of the classes and properties are taken directly from the service and schema. Furthermore, all marshalling of data is taken care of for you automatically - e.g., dates and integers. String are automatically XML encoded so even ill-formed HTML can be passed in well formed XML.
The results are not only well formed XML, but valid with respect to the schema and service definition. Always.
This example was C#. But it could have just as easily been Java, VisualBasic, JScript, Perl, or any other language for which there is a toolkit that understands WSDL document literal definitions.
If you compare the amount of code it takes to directly invoke a SOAP web service without the benefit of a WSDL description and the amount of code it takes to produce and traverse and arbitrary XML document, the difference is significant. Even more importantly, developers can immediately program using native data types with the necessary conversions taking place transparently.
The goal here is to drive integration costs to as close to zero as is humanly possible.
At the moment, the norm is for somebody to define an interchange format and evangelize it. Then a set of very smart people undertake to build individual bindings to each programming language. Only then is the application accessible to a wide range of developers.
The alternative that WSDL provides is that there can be a single definition of an interface. One that you can author in notepad, create by filling in a form in a Wizard, or reverse engineer from an existing implementation. This single definition can then be used to generate - either statically or dynamically - the necessary set of infrastructure to enable seamless integration across programming environments.
None of this is done at the expense of the Web Monkeys like me who often like to produce their XML with printfs, and scan it with regex'es. Such implementations will interoperate too.