What to use SOAP or REST

Choosing between SOAP and REST style web services for an architectural solution should depend on the consumers of the service in my opinion.  To help me make the right decision I decided to draw up a comparison matrix below showing the pros and cons of each service style.

Feature SOAP REST
Development effort

Having comprehensive toolkits  make development easier.

Toolkits are not required.  However additional work is required to map URI paths the specific handlers.
Describing available Interface definitions. Generally a WSDL is available to describe the available contracts and by using client tools proxies maybe easily generated A document is normally manually written and is  published as a web page. There is a machine readable version called WADL but is not widely used.
Message format XML based format. Has SOAP and WS-* specific markup. This can make the payload quite large. Can craft your own however common formats are XML or JASON. Does not require XML parsing.
Human readable results.
Message Transport Can use a number of transport protocols such as HTTP/S, TCP, SMTP, UDP, JMS etc Normally HTTP/HTTPS. Other protocols are supported with extra development effort.
Message contracts SOAP requires a formal contract to exist between the provider and consumer. 
If  rigid type checking is required then use SOAP.
Focus is on accessing named operations.

Has a form of dynamic contract and relies on documentation. Focus is  on accessing named resources.

Handling of complex domain objects Complex domain models can be easily represented using soap. Not so easy to handle complex models.  Excellent choice if you only require CRUD operations  over a RDBMS.
Transactional support WS* protocol supports transactions which is geared towards SOAP. Has no built in support.  The HTTP protocol cannot provide two-phase commit across distributed transactional resources.
Reliable messaging Built into the WS-* protocol. Has built in successful/retry logic. Clients need to deal with communication failures.
State management Supports both contextual information and conversation state management. The server cannot maintain any state. State management must be handled by the client
Caching No supported HTTP Get operations can be cached.
Message Encoding Supports text and binary encoding Limited to text only
Testing of services Requires unit tests to be developed or 3 rd party test tools. Can simply use a web browser and view the results. 
Security Supports enterprise security  using the WS-Security protocol. Use SOAP if intermediary devices are not trusted. Use SSL protocol for point-to-point.
Also can easily identify the intent of a request based on the HTTP verb.
Client side development complexity Toolkits are required to easily consume the service. Can consumed by any client, even a web browser using Ajax and Javascript.
Maintainability Easier to maintain due to tight data contracts and standards. In the long-run can be much expensive to maintain due to lack of standards
Popularity Mainly in enterprise applications that required WS-* features. Used by most  publically  available  web services.

My conclusion is there is no right or wrong approach for building web services with either SOAP or REST, it depends on the requirements of the consumers.

I tend to lean towards REST for CRUD type web services that integrate with websites and  SOAP for integration between critical enterprise systems that require the WS-* features such as transaction support and reliable communications.

I hope anyone reading this will find this blog helpful in making the correct architectural decision and please let me know I have left anything out.

Content based routing with WCF 4.0

This examples shows how easily it is to route messages based on message headers using WCF4

For this example I built two similar request-response WCF services with different service contract namespaces. One simply adds the two numbers together while the other service subtracts the two numbers and then returns the result.

A windows form was used as the client that accepts 2 numbers and the type of mathematical operation to perform, either addition or subtraction. Once the type of operation to perform is entered, the message header is updated with the operation and then submitted to the WCF routing service.  The routing service reads the header information and reroutes the message to the correct WCF service for the result to be displayed on the windows form.

The main focus of this blog will be the WCF routing service which I have hosted under IIS. It only consists of 2 files, the svc and the web.config file. Routing will be based on an element called “Operation” in the header section of the received soap message.

image

The svc file only contains the following entry which is very similar to a typical WCF web service except for the service model type.

<%@ ServiceHost Language="C#" Debug="true" Service="System.ServiceModel.Routing.RoutingService,System.ServiceModel.Routing, version=4.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35"  %>

 

 

The real business is in the web.config file below where the routing table and filters are defined. The key points are the routing and client endpoint sections which I will describe in more detail below.

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>

    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    <services>
      <service behaviorConfiguration="RoutingBehavior" name="System.ServiceModel.Routing.RoutingService">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration=""
           name="MathRouter" contract="System.ServiceModel.Routing.IRequestReplyRouter" />
      </service>
    </services>

    <!-- Behaviour section -->
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>


        <!-- Routing behaviour-->
        <behavior name="RoutingBehavior">
          <routing routeOnHeadersOnly="true" filterTableName="filterTable1" />
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceMetadata httpGetEnabled="true" />
        </behavior>

      </serviceBehaviors>
    </behaviors>


    <!-- Routing section-->
    <routing>
      <namespaceTable>
        <add prefix="ha" namespace="http://www.examples.com/Adder/2010/10"/>
        <add prefix="hs" namespace="http://www.examples.com/Subtractor/2010/10"/>
        <add prefix="s" namespace="http://schemas.xmlsoap.org/soap/envelope/"/>
      </namespaceTable>

      <filters>
        <filter name="AddFilter" filterType ="XPath" filterData="/s:Envelope/s:Header/ha:Operation/text() = 'Add'"/>
        <filter name="SubFilter" filterType="XPath" filterData="/s:Envelope/s:Header/hs:Operation/text() = 'Sub'"/>
      </filters>

      <filterTables>
        <filterTable name="filterTable1" >
          <add filterName="AddFilter" endpointName="Adder" priority="0"/>
          <add filterName="SubFilter" endpointName="Subtractor" priority="0"/>
        </filterTable>
      </filterTables>
    </routing>


    <!-- Client endpoints for the services-->
    <client>
      <endpoint address="http://adderservice/adder.svc" binding="basicHttpBinding" bindingConfiguration="" contract="*" name="Adder"/>
      <endpoint address="http://subtractorservice/subtractor.svc" binding="basicHttpBinding" bindingConfiguration="" contract="*" name="Subtractor"/>
    </client>
  </system.serviceModel>


  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

 

 

Below is what the soap message looks like coming over the wire using MS WCF Test Client for the Adder web service.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://EquateRequestMessage/Action</Action>
    <h:Operation i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.examples.com/Adder/2010/10" />
  </s:Header>
  <s:Body>
    <EquateRequestMessage xmlns="http://www.examples.com/Adder/2010/10">
      <Value1>0</Value1>
      <Value2>0</Value2>
    </EquateRequestMessage>
  </s:Body>
</s:Envelope>

 

In the routing section below I have used a namespace table to alias the namespace of the service contracts. Lines 2 and 3 are aliases for the WCF Services that I built. Line 4 is the default soap message namespace.

   1: <namespaceTable>

   2:         <add prefix="ha" namespace="http://www.examples.com/Adder/2010/10"/>

   3:         <add prefix="hs" namespace="http://www.examples.com/Subtractor/2010/10"/>

   4:         <add prefix="s" namespace="http://schemas.xmlsoap.org/soap/envelope/"/>

   5:       </namespaceTable>

The other section of interest is the filters. Here I am using xpath queries to read the type of mathematical operation and depending on which filter matches, it will route to the correct endpoint as defined in the filter tables. Note that I am using the namespace aliases.

   1: <filters>

   2:        <filter name="AddFilter" filterType ="XPath" filterData="/s:Envelope/s:Header/ha:Operation/text() = 'Add'"/>

   3:        <filter name="SubFilter" filterType="XPath" filterData="/s:Envelope/s:Header/hs:Operation/text() = 'Sub'"/>

   4:      </filters>

 

The filter tables determine which endpoint to reroute the message to.

   1: <filterTables>

   2:        <filterTable name="filterTable1" >

   3:          <add filterName="AddFilter" endpointName="Adder" priority="0"/>

   4:          <add filterName="SubFilter" endpointName="Subtractor" priority="0"/>

   5:        </filterTable>

   6:      </filterTables>

 

These are the endpoint addresses for the WCF services that I had built.

   1: <!-- Client endpoints for the services-->

   2:    <client>

   3:      <endpoint address="http://adderservice/adder.svc" binding="basicHttpBinding" bindingConfiguration="" contract="*" name="Adder"/>

   4:      <endpoint address="http://subtractorservice/subtractor.svc" binding="basicHttpBinding" bindingConfiguration="" contract="*" name="Subtractor"/>

   5:    </client>

 

Thats it, routing using a configuration file. This is just one example on using the new routing features in WCF 4.0.

Enjoy.