Chapter 6 — Designing Web Services for Interoperability and Resilience

 

Patterns and Practices home

WS-I Basic Security Profile 1.0 Reference Implementation: Preview release for the .NET Framework version 2.0

Microsoft Corporation

May 2006

Summary: Chapter 6 examines the design considerations for creating interoperable Web services.

Contents

Designing Web Services for Interoperability
Designing a Web Service for Resiliency
Summary

Web service specifications are still maturing. In some cases, such as SOAP and WSDL, the specifications have been ratified through organizations such as Oasis or the W3C, and even profiled by the WS-I, but in other cases, the specification is currently in review by the WS-I, or is yet to finish review by Oasis or the W3C.

In addition, different vendors' products are currently offering different combinations of the WS* specifications. This makes it harder to interoperate with the broad set of Web service specifications. Web Services Enhancements (WSE) version 3.0 provides access to many WS* capabilities now, with further functionality provided in the release of Windows Communication Foundation (WCF).

Despite the changing landscape, there are some guidelines that you can follow now that will assist you in designing Web services that are both interoperable across different vendors' platforms and more resilient to change as products evolve.

This chapter examines how you can design interoperable Web services and demonstrates how the Sample Application supports interoperability. It also demonstrates how to implement Web services so that they are resilient to changes in the underlying platform.

Designing Web Services for Interoperability

To understand how to design interoperable Web services that conform to the Basic Security Profile 1.0, you should have knowledge of some general guidelines for Web service interoperability. This section provides a high-level introduction to some of these matters, but is not intended to be fully comprehensive, because the focus is on the Basic Security Profile 1.0.

Many of the interoperability issues you may encounter were examined during the development of the Basic Profile 1.0. For information, see Building Interoperable Web Services: WS-I Basic Profile 1.0 on MSDN at https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsvcinter/html/WSI-BP\_MSDN\_LandingPage.asp. For more information about Web service interoperability, see the Web Services Interoperability page in the Web Services Developer Center on MSDN at https://msdn.microsoft.com/webservices/building/interop/.

Contract-First Development

A common challenge you will face when integrating applications that run on different platforms is how to translate data types from one platform to another. Different programming languages and operating systems support different data types, each with different names and characteristics. For example, an integer in Visual Basic 6.0 is 16 bits; while an integer in Visual Basic .NET and Visual C# is 32 bits. Similarly, if you expose a float data type from Java, it is not clear what the corresponding data type should be in the .NET Framework.

As you combine different data types into complex structures, you are increasingly likely to encounter translation problems between platforms. Examples of complex structures include collection classes, arrays, and platform-specific data types such as datasets. Even the treatment of NULL values can vary across platforms.

To help reduce the dependence on platform-specific data types, you can adopt the approach of designing the messages, first using an XML Schema (XSD) and then generating the representative platform-specific data types based on the message schema and WSDL information.

This approach was used to create the WS-I Basic Security Profile, allowing each vendor to generate platform-specific classes based on the platform-agnostic XSD representations of messages. This approach to designing messages and related contracts for services is commonly referred to as contract-first development.

This section provides a brief overview of two approaches to contract-based development. For more comprehensive information, see the following resources:

Message Oriented Design Using XSD

XSD provides a uniform platform-agnostic way of describing data types. In the Sample Application, XSD is used to define all the messages. For example, the message sent from the Web client to the retailer to place an order for a service contains a complex type that includes a product number, quantity, and price, as shown in the following code example.

<xsd:complexType name="PartsOrderItem">
    <xsd:sequence>
        <xsd:element name="productNumber" type="tns:productNumber" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="quantity" type="xsd:nonNegativeInteger" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="price"  type="xsd:decimal" minOccurs="1" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>

You can create an XSD file visually in Visual Studio 2005, using the built-in schema editor. Figure 6.1 shows the file being created graphically in Visual Studio 2005.

Figure 6.1: Creating an XSD file graphically in Visual Studio 2005

After you create the XSD file, you need to turn it into usable classes. To help you with this task, the Microsoft .NET Framework ships with an XSD tool named Xsd.exe. If you run the Xsd.exe utility against an XSD file with the /c switch, you can generate the appropriate classes automatically. The following code example shows the .NET type created from the previous XSD example.

namespace Microsoft.Practices.WSI.SCM.BSP10.Retailer.Messages
{
    /// <remarks/>
    [XmlType(Namespace=Constants.RetailerOrderNamespace)]
    public class PartsOrderItem : IListSerializer
    {
        #region Public Fields
        /// <remarks/>
         [XmlElement( "productNumber", DataType="int")]
        public int ProductNumber;
      
        /// <remarks/>
         [XmlElement( "quantity", DataType="unsignedShort")]
        public UInt16 Quantity;
      
        /// <remarks/>
         [XmlElement( "price" )]
        public System.Decimal Price;
        #endregion

Note: The PartsOrderItem class implements the IListSerializer interface, which is used to convert messages into strings. The interface is used for logging purposes.

By using Xsd.exe, you automatically ensure that the method is prefixed with XmlTypeAttribute and XmlRootAttribute. These attributes specify how the data type appears on the wire after it is serialized and forwarded using Web services.

Note: Microsoft also ships another tool that you can use to generate classes from an XSD file; this tool is named Sample Code Generator 1.4.2.1 (also known as XSDObjectGen). There are also tools that are similar to Xsd.exe in Java-based toolkits. For IBM WebSphere, the Eclipse IDE includes a "Java Bean for XML Schema" wizard. For BEA, placing the XSD file into a schemas folder within a WebLogic Workshop project will cause objects (referred to as XML Beans) to be generated.

After the .NET classes are generated from the XSDs, you can then add them to your Web service. This is shown in the following code example of a Web service definition.

public Catalog GetCatalog([XmlElementAttribute("getCatalog", Namespace=Constants.RetailerNamespace)] GetCatalog getCatIn)

     [WebMethod(MessageName="GetCatalog")]
     [SoapDocumentMethod(Constants.RetailerGetCatalogAction, 
             RequestElementName="getCatalog",
             RequestNamespace=Constants.RetailerNamespace,
             Binding="RetailerSoapBinding",
             Use=SoapBindingUse.Literal, 
             ParameterStyle=SoapParameterStyle.Bare)]
       [return: XmlElement("getCatalogResponse", Namespace=Constants.RetailerNamespace)]
       public Catalog GetCatalog([XmlElementAttribute("getCatalog", Namespace=Constants.RetailerNamespace)] GetCatalog getCatIn)

The Retailer service uses a single input parameter (GetCatalog) and uses the SoapDocumentMethod to specify that the message should be formatted as Document. It also uses the SoapBindingUse parameter to specify an encoding type of Literal and the SoapParameterStyle parameter to specify that parameters do not need to be encapsulated within one XML element within the Body element. Specifying Document / Literal / Bare ensures that the schema elements created are as expected by the schema author.

The WSDL service contract can be viewed by the .asmx file in a browser by appending "?wsdl" (without the quotation marks) to the URL.

While this approach to contract-based development is simpler than manually creating WSDL, it does still require developers to understand additional .NET attributes such as XmlRoot, XmlType, XmlElement, and the SoapDocumentMethod.

Contract Design Using XSD and WSDL

As an alternative to the message-based contract design approach, you can define the complete WSDL contract. This involves defining the service's operations at an abstract level (referred to as a WSDL PortType) using XSD messages and then defining the binding to a particular transport.

After you create the appropriate WSDL information, you can use a tool to implement the corresponding Web service interfaces. The Microsoft .NET Framework SDK includes a tool named Wsdl.exe for this purpose. The tool generates a Web service stub-class that implements the interface and a Web service proxy class that is used by clients.

Note: This approach to developing service contracts was used by the WS-I Sample Application Working Group. Each vendor is able to take the WSDL and generate platform-specific service interfaces and client proxies.

Visual Studio 2005 does not include a WSDL designer, but any XML or text editor can be used for this purpose. To create your own WSDL, you will need to have a comprehensive understanding of the language and knowledge of how the WSDL is generated into .NET code. For more information about manually creating your Web service contracts, see the following resources:

Using Software Factories

Software factories provide a set of proven practices for building specific types of applications, such as service-oriented applications. These practices are exposed through architectural overviews, patterns, how-to topics, reference implementations, automated guidance packages, and application blocks. Software factories can be used as-is or they can be customized to better meet the needs of each organization or project. By using such a toolkit, architects and developers can build solutions that are more consistent and of higher quality with less effort, and they can focus their efforts on business logic instead of implementing generic infrastructure code.

For an early look at the Web Service Software Factory, join the Service Factory community at https://practices.gotdotnet.com/projects/svcfactory. In addition to guidance relating to service-oriented applications, this community also provides Web service security and interoperability guidance.

WS-I Test Tools

The WS-I test tools are used to test Web service artifacts to ensure conformance with particular profiles. Conformance is important for making sure that Web services are interoperable.

Basic Profile 1.1 Test Tools

The WS-I developed two test tools for use with the Basic Profile 1.1, a monitor and an analyzer, that are used in conjunction with each other for evaluating whether a Web service follows the Basic Profile guidelines. Both C# and Java versions of the tools are available. The tools do the following:

  • The monitor captures and logs the messages between a Web service and a consumer.
  • The analyzer examines the messages logged by the monitor and analyzes them to determine whether a Web service follows the Basic Profile guidelines.

Figure 6.2 illustrates the relationship between the monitor and the analyzer.

Figure 6.2: The WS-I test tools

Basic Security Profile 1.0 Test Tools

A simple "man-in-the-middle" interceptor, as used in the Basic Profile 1.1 test tools is not useful if encryption has been applied to portions of the SOAP envelope. Encryption changes the nature of the SOAP message so that unintended parties cannot read the content, meaning that the secured SOAP message is no longer obviously related to the original WSDL description, and is not intelligible without decryption. The WS-I Testing Tools team is currently developing an alternative approach for testing messages secured using WS-Security for conformance with the Basic Security Profile 1.0.

Designing a Web Service for Resiliency

Web service specifications continue to evolve and mature. Some specifications, such as WS-Security, have been adopted by multiple vendors and are in the process of being profiled by the WS-I, while other specifications are still going through the standards process pending broader adoption.

The Web Services Enhancements (WSE) for Microsoft .NET builds on the Microsoft .NET Web services foundation. As these advancements gain broad adoption and mature, they will be absorbed into the .NET Framework and Visual Studio. Code written for this version of the WSE is not guaranteed to be compatible with the future versions of the product. In other words, backward compatibility cannot be assured from version to version.

Note: For a complete description of the WSE support strategy, see "Getting Started (WSE for Microsoft .NET)" on MSDN at https://msdn.microsoft.com/library/?url=/library/en-us/wse/html/f991ad3d-f574-4085-8a61-98326ff206ed.asp.

As a result of the potential for change in WSE, developers can consider writing their services in such a way that they are more resilient to change — in particular, focusing on minimizing the impact of changes to their business logic. This section provides some examples of how such techniques were applied in the Sample Application.

There are a number of measures you can take, using current technologies, to help make your Web services interoperable and continue to be interoperable in the future. By designing your Web services according to a set of guidelines, you can increase platform interoperability now, and help ensure that the services business logic is more resilient to change as the underlying transports change from WSE 3.0 to WCF.

WSE 3.0 and WCF Interoperability

The Sample Application is currently written using WSE 3.0. The following guidelines should be noted for interoperability between current and future Microsoft products:

  • Microsoft intends to provide wire-level interoperability between WSE 3.0 and WCF.
  • There will not be a way to automatically migrate source code from WSE 3.0 to WCF.

Changes within WS-Addressing, WS-Trust, and WS-Secure Conversation may lead to interoperability issues between WSE 3.0 and later releases of products that incorporate this functionality.

Separation of Concerns

Separation of concerns is a core principle of software engineering, based on the concept that software should be developed so that different or unrelated responsibilities are separated from each other and can, as a result, vary independently. Design patterns often enforce the concept of separation of concerns.

This section includes some principals that have been applied to the Sample Application to allow the services to be robust and adapt to changes in the underlying products.

Separate Business Logic from Underlying Transport Using the Service Interface Pattern

You can separate the business logic from the underlying transport by using the Service Interface pattern. This pattern allows you to protect your business logic from a dependence on a transport that may change over time. Examples of how this pattern could be used include:

  • Creating new service interfaces using WCF, replacing your WSE 3.0 services with minimal impact on your business logic.
  • Creating multiple service interfaces that each support different transport capabilities and are interoperable on a subset of your applications.

The following code example shows how the Retailer Web service reduces the dependence between the service's interface and the business logic, by factoring the business logic into a separate class, CatalogBusinessAction.

[WebMethod(MessageName="GetCatalog"]
[SoapDocumentMethod(Constants.RetailerGetCatalogAction, 
       RequestElementName="getCatalog",
       RequestNamespace=Constants.RetailerNamespace,
       Binding="RetailerSoapBinding",
       Use=SoapBindingUse.Literal, 
       ParameterStyle=SoapParameterStyle.Bare)]
    [return: XmlElement("getCatalogResponse", Namespace=Constants.RetailerNamespace)] 
    public Catalog GetCatalog([XmlElementAttribute("getCatalog", Namespace=Constants.RetailerNamespace)] GetCatalog getCatIn)
    {
try
{
    return CatalogBusinessAction.GetCatalog( getCatIn );
}
catch( RetailerException clientException )
{
    throw SoapUtility.GenerateSoapClientException( clientException );
}
catch( Exception serverException )
{
    throw SoapUtility.GenerateSoapServerException( serverException );
}
    }

Note: To maximize separation, you can also have your business logic deployed in separate DLLs from other code. The Sample Application has separate DLLs for both the business logic and the service's interface.

For more information about the Service Interface pattern, see "Service Interface" on MSDN at https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpatterns/html/DesServiceInterface.asp.

Separate Cross-Cutting Concerns

Although many of the requirements of any complex system will be independent of one another, there are also likely to be other requirements that cut across multiple aspects of the system. Examples of such cross-cutting concerns include security requirements, logging, performance, and storage management. By centralizing these concerns, you can maximize their reuse and centrally update components when they change.

Implementing Security Requirements Using Policy

WSE 3.0 allows security requirements to be verified and applied in one of the following ways:

  • Imperatively. A comprehensive set of APIs can be used by developers to secure a message and to verify that a received message is secured.
  • Declaratively. Security requirements can be specified in XML files and edited independently of your business logic or your service's interface. Visual Studio 2005 includes a wizard to automatically generate the policy files from a Web service.

Migration from WSE 3.0 to WCF will be simplified because declarative policy is completely independent of a services' business logic.

There is also tooling such as the Policy Advisor for WSE 3.0. This tool checks policy for known bad practices. This will help simplify your security reviews — because reviewing policy is much simpler than reviewing dozens of lines of code. For more information about the Policy Advisor, see Chapter 5, "Policy Usage in the Sample Application."

Centralizing Web Service Fault Handling

In addition to trapping faults that occur within business logic, you can also use a custom fault handler to declaratively handle the management of SOAP faults, including logging, in a standard way.

If you centralize logging of SOAP faults, you reduce the dependency of other code on this functionality. By doing this, you can improve the functionality more easily as the platform evolves.

Centralizing Logging Functionality

Logging is another common cross cutting-concern — supporting an organization's requirement for auditing. The Sample Application uses a SoapExtension for logging SOAP messages. The following code example shows the SoapExtension.

Microsoft.Practices.WSI.SCM. BSP10.Utilities.InterceptorTrace

The logging functionality is disabled by default, but it can be enabled by simply removing the XML comment. It is used for two reasons:

  • During interoperability testing between different vendors, this allows a record of requests and responses to be captured so that you can troubleshoot interoperability issues.
  • The graphical application monitor (see the GotDotNet Workspace for the Sample Application at https://go.microsoft.com/fwlink/?LinkId=47780) also uses this capability to allow people to observe the secured messages on the wire.

The following code example shows SOAP messages logged to the TraceLog database.

<system.web>       
    <webServices>
        <soapExtensionTypes>
            <add type="Microsoft.Practices.WSI.SCM.BSP10.Utilities.InterceptorTrace, Microsoft.Practices.WSI.SCM.BSP10.Utilities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" priority="1" group="0" />
        </soapExtensionTypes>
    </webServices>

Centralize Exception Handling

Distributed applications can have different requirements for returning fault information to users. This frequently depends on whether the fault was expected. In many cases, a data validation fault is something that a service may expect to have to process and return a detailed error to the user, but an unexpected internal exception is something that the service may want to log and then return only a standard cleansed fault to the user.

To handle these different requirements, you can either repeat your exception handling logic within each Web service or you can create different custom classes for exception shielding. These classes can be updated independently of the business logic if the underlying transports requirements change, or even if your requirements for shielding exceptions change. The following code shows how client and server exception managers are used, depending on whether the exception is planned.

try
{
    return CatalogBusinessAction.GetCatalog( getCatIn );
}
catch( RetailerException clientException )
{
    throw SoapUtility.GenerateSoapClientException( clientException );
}
catch( Exception serverException )
{
    throw SoapUtility.GenerateSoapServerException( serverException );
}
    }

Summary

With Web services specifications in varying states of maturity, and different vendors using different combinations of the WS* specifications, it can be a significant challenge to provide interoperability between Web services provided by different vendors. There is also the challenge of providing interoperability between the current implementation of WS* specifications provided in WSE 3.0 and future implementations that will be provided in WCF.

This chapter has provided guidance to help you design Web services that are interoperable across different vendors' platforms now and that are more likely to be interoperable as products change.

Start | Previous | Next

Patterns and Practices home