Web Services Description Language (WSDL)

An overview of the WSDL specification.

Posted on July 12, 2013 by Ernesto Garbarino

Overview

A WSDL is an XML-based specification that serves to describe and define web services. It is commonly used to describe SOAP-based web services. A WSDL defines an abstract definition (logical) and a concrete definition (implementation).

The above diagram illustrates the logical organisation of a WSDL 2.0 document using an “Exchange Rate Service” as an example. The Exchange Rate Service may be illustrated in Java as follows:

// Types 

class ExRateType {
   double quantity;
   String from;
   String to;
}

class ExRateError extends Exception {
   ...
}

// Operation

double getExchangeRate (ExRateType input) throws ExRateError;

The use of an enumerated type for the currencies in question (from and to variables) has been omitted for simplicity.

Abstract Definition

The abstract definition involves two parts:

  1. Type definition: The definition of types to be used during data exchange.
  2. Interface definition: The arrangement of such types into logical operations that contain inputs, outputs and faults.

Type Definition

Types are not necessarily defined within a WSDL xml document; XSD files may be referred externally using include directives. Organisations that attempt to define a canonical object model often follow this practice.

Interface Definition

At the top level, an interface defines a collection of operations and faults. Faults, in turn, can be referenced within operations.

Each operation is the equivalent of a method in an OOP language. Following this analogy, we would have the following equivalencies:

OOP WSDL
class type/element
method operation
method’s signature input
method’s return type output
method’s exception fault

Whereas inputs and outputs are bound directly to types, faults need to be declared separately and then referenced indirectly.

In the above diagram, the getExchangeRate operation results in the following mapping:

input -> exchangeRateRequest -> ExRateType
output -> exchangeRateResponse -> double
fault -> exchangeRateFault -> exchangeRateError -> string

The reason why faults are declared independently is that they can be referenced outside of the “operation context” as general, global exceptions.

Concrete Definition

The concrete definition places logical operations (such as getExchangeRate) and faults (such as exchangeRateFault) in the physical implementation context. There are two aspects to this:

  1. Binding: Associating the interface (operations and faults) with a specific transport type such as SOAP.
  2. Service declaration: Establishing the physical location (endpoint) in which the bound interface can be found.

How to Create a WSDL File

The definition of a WSDL document involves the following steps—assuming a document-first approach:

Define an initial WSDL and global namespaces

<?xml version="1.0" encoding="utf-8"?>  
<description 
    xmlns ="http://www.w3.org/ns/wsdl"
    targetNamespace = "http://www.tesira.com/ws/exchangeRateService" 
    xmlns:tns       = "http://www.tesira.com/ws/exchangeRateService"
    xmlns:ex        = "http://www.tesira.com/ws/exchangeRateServiceSchema"
    xmlns:wsoap     = "http://www.w3.org/ns/wsdl/soap"
    xmlns:soap      = "http://www.w3.org/2003/05/soap-envelope"
    xmlns:wsdlx     = "http://www.w3.org/ns/wsdl-extensions">
    
    <!-- To be completed in the next steps -->
    <types></types> 
    <interface></interface>
    <binding></binding>
    <service></service>

</description>

Key namespaces are:

Declare simple types

In most cases, by making aliases of primitive types:

<types>
    <xs:element name="exchangeRateResponse" type="xs:double"/>    
    <xs:element name="exchangeRateError" type="xs:string"/> 
</types>

Declare complex types

Complex types are often used to represent an entire input parameter set. For example: exchangeRateRequest:

<types>
...
    <xs:complexType name="ExRateType">     
    <xs:sequence>      
      <xs:element  name="quantity" type="xs:double"/>      
      <xs:element  name="from" type="xs:string"/>      
      <xs:element  name="to" type="xs:string"/>      
    </xs:sequence>     
    </xs:complexType> 

    <xs:element name="exchangeRateRequest" type="ExRateType"/>    
</types>

Define faults

Faults are defined by associating the types that represent faults. For example, exchangeRateFault -> exchangeRateError:

<interface name = "exRateInterface">

    <fault name    = "exchangeRateFault"
           element = "ex:exchangeRateError"/> 

</interface> 

Define operations

Operations are defined by associating inputs and outputs with types and faults with declared faults within the same <interface> block.

<interface name = "exRateInterface">
    ...
     <operation name   = "getExchangeRate" 
            pattern    = "http://www.w3.org/ns/wsdl/in-out" 
            style      = "http://www.w3.org/ns/wsdl/style/iri"
            wsdlx:safe = "true">
        <input messageLabel  = "In" 
               element       = "ex:exchangeRateRequest" />
        <output messageLabel = "Out" 
                element      = "ex:exchangeRateResponse" />
        <outfault ref="tns:exchangeRateFault" 
                messageLabel = "Out"/>
    </wsdl:operation>
</interface> 

Define a binding protocol

For example, SOAP:

<binding name="exchangeRateSOAPBinding" 
         interface="tns:reservationInterface"
         type="http://www.w3.org/ns/wsdl/soap"
         wsoap:protocol="http://www.w3.org/2003/05/soap/bindings/HTTP/">
</binding>

Declare global faults

In essence, faults that apply to all operations:

<binding name="exchangeRateSOAPBinding" ...>
    <wsdl:fault ref = "tns:exchangeRateFault" 
          wsoap:code= "soap:Sender"/>
</binding>

Bind operations

By associating them with interfaces:

    <binding name="exchangeRateSOAPBinding" ...>
        ...
        <wsdl:operation ref="tns:getExchangeRate" 
        wsoap:mep="http://www.w3.org/2003/05/soap/mep/soap-response"/>
    </binding>    

Declare services

By associating them with interfaces:

<service name="exchangeRateService" 
   interface="tns:exchangeRateInterface">
    
</service>

Define service’s bindings and endpoints

A single logical service may have multiple bindings/endpoints:

<service name="exchangeRateService" ...>
   
     <endpoint name    = "exchangeRateEndpoint" 
               binding = "tns:exchangeRateSOAPBinding"
               address = "http://www.tesira.com/ws/exchangeRateService"/>
    
</service>

Document Style

Document-style is the property of an interface that is message-centric as opposed to method-centric. A message-centric interface allows multiple request/response messages to be provided in a single interaction.

A method-centric interface (RPC-style), instead, simply projects a standard programming function—that consists of fixed parameters and a return value—over the network.

In the above example, we show how a series of RPC-style services are refactored into a document-style JSON service. A key aspect to consider regarding the getAccountDetails service example is that it may accept either just one account-related request or multiple ones. It should not be assumed that the actions attribute expects only a fixed structure consisting of creditCards, creditScore, and accountBalances. It may only take, say, creditScore, and/or other types of actions to be developed in the future.

In abstract terms, document-style messaging is a design pattern similar in spirit to that of the Command Pattern in OOP. In concrete terms, and in SOAP 1.2 in particular, document-style is the default way of encoding documents, whereas RPC-style is supported as an extension mechanism. In SOAP 1.0, instead, the document and RPC choices are more balanced without either of them appearing to be the prevalent choice.

Main differences:

Aspect RPC-Style Document Style
1:1 Mapping of regular programming functions Yes No
Latency (number of repeated invocations) Higher Lower
Benefits from canonical model/type library No Yes
Development cost/difficulty Lower Higher

Limitations

It is worth considering that a document-style request will ultimately be processed by the equivalent of a programming function that takes parameters and returns values. Such a programming function has to decide what to do with each of the encoded documents or actions.

As such, the capacity of providing services in a document-style fashion is constrained by two factors:

  1. The implementation environment.
  2. Pre-requisite data.

Implementation Environment

Let’s suppose that the integration platform consists of an ESB that wraps “legacy” APIs using SOAP, and that the discrete functions getCreditScore, getCreditCards, and getAccountBalance exist on a platform called “Mainframe System”.

In order to turn such discrete functions into a “getAccountDetails” document-style service, either one of the following must take place:

  1. The Mainframe System must be modified to implement a new function which may either act as a proxy towards the three discrete mainframe functions or it may implement the necessary business logic from scratch.
  2. The ESB must provide the capability to define a brand-new service that provides the necessary business logic to interact with the received “documents” so that discrete invocations to the Mainframe Account System may be produced.

Unless some form of caching is in place, option 2 does not necessarily reduce latency—as per the provided example—since four invocations still take place behind the scenes for every single invocation to the document-style service.

Availability of Pre-requisite data

All pre-requisite data must be handed over in a single request. If the messages have inter-dependencies, in the sense that the output of a message is the input of another, then an additional request will be required to produce such data. In fact, in the provided example, we assume that the provider somehow already “knows” which are Zizek’s account numbers.

Another alternative would be to allow the chaining of documents in the business service’s logic when their outputs and inputs coincide. For example:

{
    "customer": "Slavoj Zizek",
    "actions": [
        "creditCards",
        "creditScore",
        {
            "chain": {
                "accountNumbers": "accountBalances"
            }
        }
    ]
}

The action chain in the above example would group two other actions and take the output of the first action (accountNumbers) into the input of the second one (accountBalances).

RPC-Style

Remote Procedure Call-Style (RPC-style) is the property of an interface that is method-centric as opposed to message-centric. A method-centric interface (RPC-style), projects a standard programming function consisting of fixed parameters and return values over the network.

The RPC-style property is normally treated in the context of its opposite style; the Document-Style one.