28 January 2011

Writing a WSDL 1.1 Web Service Contract by Hand

A WSDL document may be confusing at first glance, but is a fairly simple XML document to code by hand once you've understood the concepts. This tutorial will help you writing and understanding the WSDL.

WSDL Layout
The WSDL essentially defines operations grouped as a service, the way these services are called (SOAP etc.) and where the service is hosted.

We will start by defining the service interface (what operations will be exposed) and the messages used by the operations, then will create the binding and finally the endpoint where the service is hosted.

The service create will be using the document/literal/wrapped style thats is most common for creating services that are interoperable.

Designing the Service
We first need to decide what the service will be: what operations it will be hosting:

  • Service Name: Users
  • Operations: 
    • addUser - takes user, no return
    • getUser - takes user id, returns user

Basic WSDL
The root element is 'definitions' and we will be declaring all the namespaces on the root element:

<?xml version="1.0"?> 
<wsdl:definitions name="Users" 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
    xmlns:schema="http://www.example.com/ns/user" 
    xmlns:tns="http://www.example.com/ns/user/service" 
    targetNamespace="http://www.example.com/ns/user/service" 
> 

<wsdl:documentation> 
Example Service to demonstrate WSDL authoring. 
</wsdl:documentation> 

</wsdl:definitions>

Namespaces:
  • wsdl - the WSDL namespace
  • soap - the SOAP binding
  • schema - our example data schema
  • tns - our namepsace for the WSDL

Interface Design
Here we will define the service and its operations. We will do this by using the portType element.

We will call the portType UserEI where 'EI' denotes 'Endpoint Interface'. The getUsers operation is a standard request/response type call or IN-OUT Message Exchange Pattern (MEP), the addUser operation we will make an IN-ONLY MEP.

<wsdl:portType name="UserEI">
    <wsdl:documentation>User operations</wsdl:documentation>
   
    <wsdl:operation name="getUser">
        <wsdl:documentation>Get a User by User ID</wsdl:documentation>
        <wsdl:input message="tns:getUser"/>
        <wsdl:output message="tns:getUserResponse"/>
    </wsdl:operation>

    <wsdl:operation name="addUser">
        <wsdl:documentation>Add a User</wsdl:documentation>
        <wsdl:input message="tns:addUser"/>
    </wsdl:operation>

</wsdl:portType>

Note that the messages (tns:getUser etc..) are not yet defined, see the next section.

Messages
Now we define the messages using the 'message' element. The messages are mapped to elements defined in the external XSD.

<wsdl:message name="getUser">
    <wsdl:part element="schema:getUser" name="parameters"/>
</wsdl:message>

<wsdl:message name="getUserResponse">
    <wsdl:part element="schema:getUserResponse" name="parameters"/>
</wsdl:message>

<wsdl:message name="addUser">
    <wsdl:part element="schema:addUser" name="parameters"/>
</wsdl:message>

Note that the part refers to the 'element' defined in the XSD, not the 'type', the part name is 'parameters', the element name is the same as the related operation name and there is only one part per message. These are important rules in create 'wrapped' style services.

Message Schema
As the messages can only have one part/element, this element becomes a 'wrapper' for the data 'parameters' the message contains. I'm not going to go into detail on the XSD schema, as its not the focus of this tutorial. This is how the separate XSD file could look:

<?xml version="1.0"?>
<xsd:schema 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:msg="http://www.example.com/ns/user"
    targetNamespace="http://www.example.com/ns/user"
    elementFormDefault="qualified"
    attributeFormDefault="qualified"
>

<xsd:element name="getUser" type="msg:getUser"/>
<xsd:element name="getUserResponse" type="msg:getUserResponse"/>
<xsd:element name="addUser" type="msg:addUser"/>

<xsd:complexType name="getUser">
    <xsd:sequence>
        <xsd:element name="id" type="xsd:string"/>
    </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="getUserResponse">
    <xsd:sequence>
        <xsd:element name="user" type="msg:user"/>
    </xsd:sequence>
</xsd:complexType>
 
<xsd:complexType name="addUser">
    <xsd:sequence>
        <xsd:element name="user" type="msg:user"/>
    </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="user">
    <xsd:sequence>
        <xsd:element name="id" type="xsd:string"/>
        <xsd:element name="name" type="xsd:string"/>
    </xsd:sequence>
</xsd:complexType>
<xsd:schema>

Import the data definitions
The file is imported under the same namespace we declared at the beginning of this tutorial (http://www.example.com/ns/user).

<wsdl:types> 
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <xsd:import namespace="http://www.example.com/ns/user" schemaLocation="userSchema.xsd"/> 
  </xsd:schema> 
</wsdl:types>

All that left now is to define the binding and where the service is hosted:

Binding
In this example we are going to define SOAP bindings for the service (most common binding). Note that we bind to the interface or 'portType' (tns:UserEI) we defined earlier.

We are using the 'document' style as opposed to 'RPC' and the SOAP body is 'literal' as opposed to 'encoded'.


<wsdl:binding name="UserBinding" type="tns:UserEI">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

  <wsdl:operation name="getUser">
    <soap:operation soapAction="http://www.example.com/ns/user/action/getUser"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
  </wsdl:operation>

  <wsdl:operation name="addUser">
    <soap:operation soapAction="http://www.example.com/ns/user/action/getUser"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
  </wsdl:operation>

</wsdl:binding> 

Endpoint
The last thing we need is to specify where the service is being hosted:

<wsdl:service name="UserService"> 
  <wsdl:port binding="tns:UserBinding" name="UserEndpoint"> 
    <soap:address location="http://www.example.com/services/User"/> 
  </wsdl:port> 
</wsdl:service>

Testing the WSDL
When writing something like this by hand, you will make mistakes: in writing the code above I made 4 mistakes, all relating to typos (element names not matching etc..). I checked the WSDL by running Java's wsimport tool, which takes the WSDL as input and generates Java artifacts. Here is the command I ran:

wsimport -keep -Xnocompile -s src users.wsdl

Which generates the Java source in the 'src' directory, does not compile it and keeps the source code.

You could use a tool like soapui, or an XML editor of your choice.




4 comments:

Anonymous said...

Nice job, clear and concise.
Thanks

New Mexico Service Luciana said...

Thanks alot for posting this! Made my life easier. Very well stated.

seo reseller said...

A nice Codes for writing a WSDL. Coding manually of this is much better than ready made codes.

Anonymous said...

Thanks for the tip on importing schemas through wsdl-types!