Introduction to WSDL
Recently I had to create a soap webservice. The WSDL generator put in too much, so I decided to make the WSDL myself. Luckily a colleague gave me a quick intro.
So, let's start with mentioning Geert Van Damme. He's a former Java developer who saw the light and is developing in PHP for a couple of years now. The great thing about him is that he is a true software architect and a joy to work with. He gave a small intro on creating WSDL files to the development team I'm part of and in turn I promissed I would write a blog post about it. If anything is wrong, let me know and I'll smack Geert for misinforming me. ;-)
The order of the elements is of no importance. I'm explaining the logical order here, but you will typically see it the other way around. For explaining, the approach below makes more sense in my opinion.
Definitions
The WSDL root element is called definitions and here the different namespaces used in the WSDL are defined.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:amz="http://services.amazium.com/mytest"
name="MyTest"
targetNamespace="http://services.amazium.com/mytest"
>
<!-- ... -->
</definitions>
You don't have to look at most of them. They are there for a reason, but I don't know which either. ;-)
xmlns:amz
The xmlns:amz defines the namespace you will use in the wsdl. This can be anything. I choose for amz, short for amazium. The url next to it has to be unique, but it doesn't need to exist. If you want to do it the right way, make sure you put a schema there defining what's in your namespace.
name
The name is just a name for your webservice. Again, can be anything you want.
targetNamespace
The target namespace will point to the default namespace used in your wsdl. Put the same url as in the xmlns:YOURNAME here.
Your Soap Server
One of the most important parts of the wsdl is the location of your soap server. In this article I will not explain how to create a soapserver, I'm assuming you already know.
So you define a service element and you give it a name. It's nice and clean to append it with Service, but it's not required.
Inside the service you define a port element. You need 2 attributes there: "binding" and "name". In the binding (see below), you use your namespace colon the name. The name can be anything, but usually it's appended with Soap.
Last but not least you define the server's location inside a soap:address element. This is the url to your soap server.
<service name="MyTestService">
<port binding="amz:MyTestSoap" name="MyTestSoap">
<soap:address location="http://www.hostname.com/path/to/soap/server" />
</port>
</service>
Bindings
Your soap server implements a number of functions or "operations". In the bindings you specify what they are.
You start of with a binding element. Inside you define the name (the one you specified above, in the example MyTestSoap) as an attribute. You also specify the port type (see below).
The first child element is soap:binding where you define the style used and the transport method used. I typically set these to the document style and the http soap transport (see example).
Then you have an operation element per function on your soap service. The operation element has a name which is exactly the same as the function defined in the soap service.
Inside the operation, you first put a soap:operation element. Here you put a unique url as soapAction. I typically use the namespace url, appended with the function name (see example).
You also specify an input and output element where you say define what your body looks like. I use literal which means that we define how it looks in the wsdl.
<binding name="MyTestSoap" type="tns:MyTestPortType">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" /
<operation name="getOptions">
<soap:operation
soapAction="http://services.amazium.com/mytest/getStuff" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<!-- ... -->
</binding>
Port Type
In the portType you define what elements define your input and output. So you will need an operation element for each function on your soap servers and each will have an input and output element. The name you define on your portType is the one used in the binding element's type attribute.
Both the input and output elements, have a message attribute where you define your messages. Start by using your namespace and then add your function name appended with Request or Response.
<portType name="MyTestPortType">
<operation name="getStuff">
<input message="amz:getStuffRequest" />
<output message="amz:getStuffResponse" />
</operation>
<!-- ... -->
</portType>
Messages
Last but not least you have the messages. These are defined as message elements, directly under the definitions element. So if you have 3 function, you will typically have 6 message elements.
You have 2 possibilities here: you can write out your function params or you can decide to have 1 param (an object) which you can define later on. I prefer the latter since it allows you to add extra validation. Again you have a choice to add the element description in your WSDL or in a seperate XSD file. I prefer the seperate XSD file since you could use this for other things as well. If your service is used by a java dude for instance, he can build his classes based on this xsd. To use an XSD add a types element like this:
<types>
<xsd:schema targetNamespace="http://services.amazium.com/mytest">
<xsd:include schemaLocation="mytest.xsd"/>
</xsd:schema>
</types>
Then you have your message elements. Where you define it's parts. Since I use the object option, I only have one part here:
<message name="getStuffRequest">
<part name="getStuffRequest" element="amz:getStuffRequest" />
</message>
<message name="getStuffResponse">
<part name="getStuffResponse" element="amz:getStuffResponse" />
</message>
It might get a bit confusing now since I used the same name as the message, but the amz:getStuffRequest/Response here point to an element defined in the XSD.
Types / XSD
Last but not least we define our request and response classes. This follows the typical XSD rules, if you need to know more about this I suggest you have a look on google. A quick example:
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema targetNamespace="http://services.amazium.com/mytest"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:amz="http://services.amazium.com/mytest" >
<xsd:element name="getStuffRequest" type="amz:getStuffRequestType" />
<xsd:element name="getStuffResponse" type="amz:getStuffResponseType" />
<xsd:complexType name="getStuffRequestType">
<xsd:sequence>
<xsd:element name="channel" type="amz:channelType" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="getStuffResponseType">
<xsd:sequence>
<xsd:element name="stuff" type="amz:stuffType"
maxOccurs="unbounded" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="channelType">
<xsd:annotation>
<xsd:documentation>Channel where the stuff is found</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="ABC" />
<xsd:enumeration value="DEF" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="stuffType">
<xsd:sequence>
<xsd:element name="id" type="xsd:int" />
<xsd:element name="title" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Conclusion
There are probably more, different ways to do it, but I found this one quite easy to understand and follow. If you need more info on WSDL you could have a look at the article Which style of WSDL should I use? on the IBM site or the W3C WSDL description.
Have fun!
Jeroen






+32 475 62.42.64
Joeri Cornelissens
2010-06-24 15:55