Home > Articles

.NET Web Services: Interface-Based Web Service Development

  • Print
  • + Share This
Yasser Shohoud looks at the concept of interface-based programming as it applies to .NET Web services. Learn how to develop Web services based on interfaces; how to implement multiple interfaces (bindings) using a single class and what the resulting WSDL looks like; and how clients can program against interfaces and dynamically read the Web service's URL from a configuration file rather than hard-coding it in the client.
This chapter is from the book

Chapter 8 Interface-Based Web Service Development

The goal of education is the advancement of knowledge and the dissemination of truth. —John F. Kennedy

Interface-based programming was popularized with component-based development in the 1990s. Using technologies like COM, you could define an interface and have several components implement it. Clients could utilize any of those components by programming against the interface. As your Web services evolve and mature, you will find it necessary to factor out Web service methods into interfaces, implement existing standard interfaces on your Web services, and program clients against an interface rather than a specific Web service. Interfaces can also be useful for versioning Web services by leaving the old interface intact and implementing a new interface on the same service.

WSDL bindings make this possible. In Chapter 4 you learned about WSDL bindings and how they define a concrete set of operations and provide the information needed to invoke those operations. A Web service implements one or more bindings and exposes them at a particular location defined by the port. Even if you haven't read Chapter 4, you can read this chapter and learn how to do interface-based programming. However, you will gain much more from this chapter if you read Chapter 4 first.

8.1 Defining Interfaces

The first step in interface-based programming is to define the interfaces you want to implement. When you build a Web service, you should always start with defining the interface. Today, tools like Visual Studio .NET do not provide direct support for this. I am hopeful that future versions will provide the needed support for defining Web service interfaces.

Although you can use Notepad to create a WSDL document from scratch, you'll probably want a more productive and less error-prone way to define your interfaces. An easy way to define a Web service interface is to create a Web service and define the Web methods you want the interface to have. If you have parameters with complex types, you define those types in schemas, then use xsd.exe to generate classes from the schemas (see Chapter 2 for more information on xsd.exe).

By default, all of a Web service's methods belong to the same binding. That binding (interface) has the same name as the Web service class with the word Soap appended. If you've created COM components in Visual Basic, you may know that each component you create has a default interface that is given the name _ClassName. Therefore, the concept of auto-generated interfaces shouldn't be new to you.

To control the binding's name and namespace, you use the WebServiceBinding attribute on the Web service class to specify that binding's name and namespace. On each Web method that the service exposes, you add SoapDocumentMethod or SoapRpcMethod and set its Binding property to the binding name. Listing 8.1 shows an example class called SupplierIface1 that exposes its methods in a binding called ISupplier.

Listing 8.1 A Web service example that exposes a binding called ISupplier (VBWSBook\Chapter8\Supplier1.asmx.vb)

Namespace Supplier1
    Public Structure Order
        Public CustomerEmail As String
        Public ShipVia As Shipper
        Public ShipName As String
        Public ShipAddress As String
        Public ShipCity As String
        Public ShipState As String
        Public ShipZipCode As String
        Public OrderItems() As OrderItem 'array of OrderItems
    End Structure
    Public Structure OrderItem
        Public ProductID As Integer
        Public Quantity As Integer
    End Structure
    Public Enum Shipper
        FedEx = 1
        UPS
        USPS
    End Enum
    Public Enum OrderStatus
        Pending
        Shipped
        Delivered
    End Enum
    Public Structure OrderInfo
        Public Status As OrderStatus
        Public ShippingType As String
        Public DeliveredDate As Date
        Public DeliveredTo As String
    End Structure
    Public Structure QuoteInfo
        Public ProductCost As Double
        Public Tax As Double
        Public Shipping As Double
        Public TotalCost As Double
    End Structure
    <WebServiceBinding( _
    Name:="ISupplier", _
    [Namespace]:="http://LearnXmlWS.com/Supplier"), _
    WebService([Namespace]:="http://LearnXmlWS.com/Supplier", _ 
     Description:="The supplier's Web service")> _
    Public Class SupplierIface1
        Inherits System.Web.Services.WebService
        <WebMethod( _ 
            Description:= _ 
            "Places the order then returns the new order id"), _
        SoapDocumentMethod(Binding:="ISupplier")> _
        Public Function PlaceOrder(ByVal newOrder As Order) _
                                   As String
            'returns a new order id
        End Function
        <WebMethod(), _
        SoapDocumentMethod(Binding:="ISupplier")> _
        Public Function CheckStatus(ByVal OrderId As String) _
                                    As OrderInfo
            'returns an orderinfo structure
        End Function
        <WebMethod(), _
        SoapDocumentMethod(Binding:="ISupplier")> _
        Public Function GetPriceQuote(ByVal newOrder As Order) 
                                      As QuoteInfo
            'returns an orderinfo structure
        End Function

    End Class
End Namespace

The first part of Listing 8.1 defines the data types that will be used by the service methods (for example, Order, OrderItem, Shipper, OrderStatus, OrderInfo, and QuoteInfo). The SupplierIface1 class has two attributes applied to it. WebServiceBinding has its name property set to ISupplier and its namespace property set to http://LearnXmlWS.com/Supplier. Each of the Web service methods has a SoapDocumentMethod applied to it with the Binding property set to ISupplier, making the methods part of the interface called ISupplier. The resulting WSDL document contains the binding definition and the service definition.

To get a pure interface definition, you can save this WSDL document to disk and remove the <service> element, which specifies a particular implementation for the interface. The edited WSDL document, shown in Listing 8.2 now contains your interface definition, which you can give to other developers who can use it to implement the same binding (interface) on their services.

Listing 8.2@The interface WSDL after removing the <service> element (VBWSBook\Chapter8\SingleInterface.wsdl)

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:s0="http://LearnXmlWS.com/Supplier"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
targetNamespace="http://LearnXmlWS.com/Supplier" 
xmlns="http://schemas.xmlsoap.org/wsdl/">
  <types>
    <s:schema elementFormDefault="qualified" 
        targetNamespace="http://LearnXmlWS.com/Supplier">
      <s:element name="PlaceOrder">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" 
                       name="newOrder" type="s0:Order" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="Order">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" 
                     name="CustomerEmail" type="s:string" />
          <s:element minOccurs="1" maxOccurs="1" 
                     name="ShipVia" type="s0:Shipper" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="ShipName" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="ShipAddress" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="ShipCity" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="ShipState" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="ShipZipCode" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="OrderItems" 
                     type="s0:ArrayOfOrderItem" />
        </s:sequence>
      </s:complexType>
      <s:simpleType name="Shipper">
        <s:restriction base="s:string">
          <s:enumeration value="FedEx" />
          <s:enumeration value="UPS" />
          <s:enumeration value="USPS" />
        </s:restriction>
      </s:simpleType>
      <s:complexType name="ArrayOfOrderItem">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" 
                     name="OrderItem" type="s0:OrderItem" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="OrderItem">
        <s:sequence>
          <s:element minOccurs="1" maxOccurs="1" 
                     name="ProductID" type="s:int" />
          <s:element minOccurs="1" maxOccurs="1" 
                     name="Quantity" type="s:int" />
        </s:sequence>
      </s:complexType>
      <s:element name="PlaceOrderResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" 
                       name="PlaceOrderResult" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="CheckStatus">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" 
                       name="OrderId" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="CheckStatusResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" 
                       name="CheckStatusResult" 
                       type="s0:OrderInfo" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="OrderInfo">
        <s:sequence>
          <s:element minOccurs="1" maxOccurs="1" 
                     name="Status" type="s0:OrderStatus" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="ShippingType" type="s:string" />
          <s:element minOccurs="1" maxOccurs="1" 
                     name="DeliveredDate" type="s:dateTime" />
          <s:element minOccurs="0" maxOccurs="1" 
                     name="DeliveredTo" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:simpleType name="OrderStatus">
        <s:restriction base="s:string">
          <s:enumeration value="Pending" />
          <s:enumeration value="Shipped" />
          <s:enumeration value="Delivered" />
        </s:restriction>
      </s:simpleType>
      <s:element name="GetPriceQuote">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="newOrder"
                      type="s0:Order" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="GetPriceQuoteResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" 
                       name="GetPriceQuoteResult" 
                       type="s0:QuoteInfo" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="QuoteInfo">
        <s:sequence>
          <s:element minOccurs="1" maxOccurs="1"
                     name="ProductCost" type="s:double" />
          <s:element minOccurs="1" maxOccurs="1" 
                     name="Tax" type="s:double" />
          <s:element minOccurs="1" maxOccurs="1" 
                     name="Shipping" type="s:double" />
          <s:element minOccurs="1" maxOccurs="1" 
                     name="TotalCost" type="s:double" />
        </s:sequence>
      </s:complexType>
    </s:schema>
  </types>
  <message name="PlaceOrderSoapIn">
    <part name="parameters" element="s0:PlaceOrder" />
  </message>
  <message name="PlaceOrderSoapOut">
    <part name="parameters" element="s0:PlaceOrderResponse" />
  </message>
  <message name="CheckStatusSoapIn">
    <part name="parameters" element="s0:CheckStatus" />
  </message>
  <message name="CheckStatusSoapOut">
    <part name="parameters" element="s0:CheckStatusResponse" />
  </message>
  <message name="GetPriceQuoteSoapIn">
    <part name="parameters" element="s0:GetPriceQuote" />
  </message>
  <message name="GetPriceQuoteSoapOut">
    <part name="parameters" element="s0:GetPriceQuoteResponse" />
  </message>
  <portType name="ISupplier">
    <operation name="PlaceOrder">
      <documentation>
        Places the order then returns the new order id
      </documentation>
      <input message="s0:PlaceOrderSoapIn" />
      <output message="s0:PlaceOrderSoapOut" />
    </operation>
    <operation name="CheckStatus">
      <input message="s0:CheckStatusSoapIn" />
      <output message="s0:CheckStatusSoapOut" />
    </operation>
    <operation name="GetPriceQuote">
      <input message="s0:GetPriceQuoteSoapIn" />
      <output message="s0:GetPriceQuoteSoapOut" />
    </operation>
  </portType>
  <binding name="ISupplier" type="s0:ISupplier">
    <soap:binding 
      transport=
      "http://schemas.xmlsoap.org/soap/http" style="document" />
    <operation name="PlaceOrder">
      <soap:operation 
        soapAction="http://LearnXmlWS.com/Supplier/PlaceOrder" 
        style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
    <operation name="CheckStatus">
      <soap:operation 
        soapAction="http://LearnXmlWS.com/Supplier/CheckStatus" 
        style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
    <operation name="GetPriceQuote">
      <soap:operation 
          soapAction=
          "http://LearnXmlWS.com/Supplier/GetPriceQuote" 
          style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
  </binding>
</definitions>
  • + Share This
  • 🔖 Save To Your Account