18.4. Understanding WSDL and SOAP

 < Day Day Up > 

The purpose of this section is to add some depth to the topic of Web Services to look underneath at the XML grammar that is used to describe the service (WSDL) and the protocol (SOAP) used for transporting data between the service and client. Be aware that both of these have been submitted to the World Wide Web Consortium (W3C), but neither is an official standard.

Web Services Description Language (WSDL)

WSDL is broadly defined as "an XML format for describing network services as a set of endpoints operating on messages containing either document-oriented or procedure-oriented information."[2] In our case, the endpoints are the client and a Web Service, and WSDL defines how the client interacts with the service.

[2] Web Services Descriptive Language (WSDL) 1.1 W3C Note, March 15, 2002.

When developing a Web Service from the ground up, it is good practice to develop the interface definition first in the form of WSDL and then map it to the implementation code. Although the sheer complexity of WSDL demands that WSDL editors be used for the task, there is still a need for the developer to have a general understanding of the WSDL structure. The same is true even if you work only on the client side and use wsdl.exe to create source code directly from the WSDL. Problems can arise using utilities and editors that require a familiarity with the format in order to perform troubleshooting. For example, a well-designed WSDL interface is often built from multiple documents tied together by an XML import element. Being familiar with the semantics and existence of the import element goes a long way toward solving import issues a not uncommon source of WSDL errors.

This section introduces the basic structure of WSDL, which should satisfy the curiosity of the occasional Web Service consumer or developer. Those interested in learning more should refer to the specification that is published by W3C at http://www.w3.org/TR/wsdl.

The WSDL Structure

Specifications for the WSDL grammar define six major elements: definitions, types, message, port type, binding, and service. Let's discuss these within the context of the WSDL file that describes the sample BirthDayWS Web Service.

<Definitions>

This is the root element of the WSDL document. It declares multiple namespaces used throughout the document, and contains all of the other elements:

 <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://tempuri.org/"    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://tempuri.org/"    xmlns="http://schemas.xmlsoap.org/wsdl/"> 

Namespaces are used to distinguish elements because it is possible that elements from different namespaces could have the same name.

<Types>

This element contains an XSD (XML Schema Definition Language) schema that describes the data types publicly exposed by the service: the parameters passed in the Web Service request, and the response:

 <types>    <s:schema elementFormDefault="qualified"        targetNamespace="http://tempuri.org/">    <s:element name="GetDayBorn">    <s:complexType>    <s:sequence>    <s:element minOccurs="1" maxOccurs="1" name="mo"       type="s:int" />    <s:element minOccurs="1" maxOccurs="1" name="day"       type="s:int" />    <s:element minOccurs="1" maxOccurs="1" name="yr"       type="s:int" />    </s:sequence>    </s:complexType>    </s:element>    <s:element name="GetDayBornResponse">    <s:complexType>    <s:sequence>    <s:element minOccurs="0" maxOccurs="1" name="GetDayBornResult"       type="s:string" />    </s:sequence>    </s:complexType>    </s:element>    </s:schema> </types> 

<Message>

Defines the data that is exchanged between the Web Service provider and consumer. Each message is assigned a unique name and defines its parameters if any in terms of names provided by the types element:

 <message name="GetDayBornSoapIn">    <part name="parameters" element="s0:GetDayBorn" />    </message>    <message name="GetDayBornSoapOut">    <part name="parameters" element="s0:GetDayBornResponse" />    </message> 

<PortType>

Each <portType> element defines the <Message> elements that belong to a communications transport. The name attribute specifies the name for the transport. The <portType> element contains <operation> elements that correspond to the methods in the Web Service. The <input> and <output> elements define the messages associated with the operation. Four types of operations are supported: one-way, in which the service receives a message; request-response, in which the client sends a request; solicit-response, in which the service first sends a message to the client; and notification, where the service sends a message to clients.

 <portType name="BirthDaySoap">    <operation name="GetDayBorn">    <documentation>Return day of week for any date</documentation>    <input message="s0:GetDayBornSoapIn" />    <output message="s0:GetDayBornSoapOut" />    </operation> </portType> 

<Binding>

A set of rules that describe how the <portType> operation is transmitted over the wire. Wire protocols available are HTTP GET, HTTP POST, and SOAP. This example demonstrates how SOAP is specified.

As acknowledgement of the importance of SOAP as a transport protocol, the WSDL 1.1 specification includes extensions for SOAP 1.1. These extension elements include <binding>, <operation>, and <body>.

 <binding name="BirthDaySoap" type="s0:BirthDaySoap">    <soap:binding transport=       "http://schemas.xmlsoap.org/soap/http"       style="document" />    <operation name="GetDayBorn">       <soap:operation soapAction="http://tempuri.org/GetDayBorn"          style="document" />       <input>          <soap:body use="literal" />       </input>       <output>          <soap:body use="literal" />       </output>    </operation> </binding> 

Note that the <operation> element specifies the entry point for the Web method that is called on the server. One other thing to be aware of is the style attribute in the binding element. This value, which may be document or rpc, specifies how an operation is formatted. By default, .NET sets this value to document. To specify rpc, you must apply the SoapRpcMethodAttribute to the Web method:

 [SoapRpcMethod][WebMethod] public string GetDayBorn(string month, int day, int yr) 

Although there is a rather spirited debate among WSDL purists as to which is better, you can safely ignore the histrionics and use the .NET default. However, knowing your options will enable you to easily work with third parties that may have a preference.

<Service>

Identifies the location of the Web Service. Specifically, it lists the name of the Web Service class, the URL, and references the binding for this endpoint.

 <service name="BirthDay">    <port name="BirthDaySoap" binding="s0:BirthDaySoap">       <soap:address location=       "http://localhost/ws/BirthDayWs.asmx" />    </port> </service> 

Simple Object Access Protocol (SOAP)

SOAP is a platform-neutral protocol for exchanging information. Its cross-platform capabilities are attributable to its use of XML to define the data being passed and support for HTTP as a communications protocol. SOAP is the most popular and flexible protocol for the exchange of information between Web Service consumers and providers. Its format allows it to define complex data structures not supported by the competing HTTP GET and POST protocols.

Our discussion of SOAP follows the same approach used with WSDL: We examine the basic features of SOAP using the request/response messages generated from the BirthDayWS Web Service example. The format of these messages is described on the Web page containing the desired method(s) of the Web Service.

A SOAP Request Message

The header for a SOAP request reveals that the SOAP request is packaged as an HTTP POST request to the server designated by the Host field. The length field specifies the number of characters in the body of the POST, and SOAPAction indicates the namespace and method to be contacted.

 POST /ws/BirthDayWS.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://tempuri.org/GetDayBorn" 

Listing 18-3 shows the XML template for the SOAP message that is sent to the server.

Listing 18-3. GetdayBorn SOAP Request Content

 <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001 /XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http:/ /tempuri.org/" xmlns:types="http://tempuri.org/encodedTypes" xmlns:soap="http://schemas .xmlsoap.org/soap/envelope/">    <soap:Body       soap:encodingStyle=          "http://schemas.xmlsoap.org/soap/encoding/">       <tns:GetDayBorn>          <mo xsi:type="xsd:int">int</mo>          <day xsi:type="xsd:int">int</day>          <yr xsi:type="xsd:int">int</yr>       </tns:GetDayBorn>    </soap:Body> </soap:Envelope> 

The overall structure of a SOAP message is not complex. It is an XML document that has a mandatory root element, <Envelope>, an optional <Header> element, and a mandatory <Body>.

A SOAP envelope, as the name implies, is conceptually a container for the message. The SOAP header represents a way to extend the basic message. It may contain additional information about the message and as we will see later can be used to add a measure of security. The SOAP body contains what one would regard as the actual data: the arguments sent to the service and the response. The contents of the <Body> element in this example consist of the method name and its three parameters that correspond to the call made within the client code:

 string dayOfWeek = bd.GetDayBorn(12,20,1963); 

A SOAP Response Message

The SOAP body of the response includes a <GetDayBornResult> element (see Listing 18-4) that contains the response from the Web Service and identifies it as a string type.

Listing 18-4. GeTDayBorn SOAP Response Content
 <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:soap- enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://tempuri.org/" xmlns:types="http://tem- puri.org/encodedTypes" xmlns:soap="http://schemas.xml- soap.org/soap/envelope/">    <soap:Body       soap:encodingStyle=          "http://schemas.xmlsoap.org/soap/encoding/">       <tns:GetDayBornResponse>          <GetDayBornResult             xsi:type="xsd:string">string</GetDayBornResult>       </tns:GetDayBornResponse>    </soap:Body> </soap:Envelope> 

Using the SOAP Header for User Authentication

The optional SOAP header is available for adding miscellaneous information about its associated SOAP message. One popular use for this header is to include identification information about the user making the request. This enables user authentication to be performed by the methods within the Web Service.

Core Note

A question that arises when a Web Service is being invoked from a Web page is whether Forms Authentication (Chapter 17) can be used. The short answer is yes, but it requires a special implementation. Unlike a regular Web page, a Web Service does not recognize the authentication cookie created by the separate login screen. The solution is to add a login method to the Web Services that creates the authentication cookie. In addition, each service must check user authentication before performing its operation. The coding requirements are comparable to using SOAP header authentication.


Listing 18-5 demonstrates a Web Service that checks the header for an ID and password before making the service available to the requestor. It consists of the code taken from Listing 18-1 and updated to include features required to access a SOAP header.

Listing 18-5. Authenticate Web Service User by Checking SOAP Header
 using System; using System.Web.Services; // Required for SoapException using System.Web.Services.Protocols; namespace BirthDayWS { // (1) Class to hold data passed in the header    public class SOAPHeaderContent : SoapHeader    {       public string UserName;       public string PassWord;    }    public class BirthDay : System.Web.Services.WebService    {       // (2) Member class accessing header data       public SOAPHeaderContent headerInfo;       // (3) Add SoapHeader attribute       [WebMethod(Description="Return day of week for any date"),             SoapHeader("headerInfo")]       public string GetDayBorn(int mo, int day, int yr)       {          if (!Verify())          {             throw new SoapException(                   "Valid User info not included.",                   SoapException.ClientFaultCode);          } else {             bool err = false;             string dob;             if (mo <1 || mo >12) err=true;             if (day < 1 || day > 31) err=true;             if ( ! err)             {                DateTime dt = new DateTime(yr,mo,day);                dob = dt.ToString("dddd"); // extracts day             }  else {                dob = "Invalid Date";             }             return(dob);          }       }       // Method to check password and ID in SOAP header       private bool Verify()       {          // (4) Access data in header          if (headerInfo.UserName != "Vincent" ||              headerInfo.PassWord != "arles")          { return(false);          } else { return(true);}       }    } } 

This example illustrates the four steps that are followed to receive and access any SOAP header data:

1.

Create a class to represent the data in the header. This class must inherit from the SoapHeader class. SOAPHeaderContent serves the purpose in this code.

2.

Add a member to the Web Service class that is the same type as the class created in Step 1. The example uses headerInfo.

3.

Apply a SoapHeader attribute that references the member created in Step 2. Applying this to a method makes the information in the header available to the method.

4.

Process the header data by accessing the members of the class created in Step 1. The Verify method contains the simple logic to check PassWord and UserName. In reality, the comparison information would come from a database rather than being hardcoded.

The proxy for this Web Service includes the class that represents the header contents. The client creates an instance of this class and assigns a password and user name. The class instance is assigned to a field that is now a member of the proxy class representing the Web Service class. In this example, Birthday now has a field named SOAPHeaderContentValue. As you have probably guessed, .NET creates this field name by appending Value to the name of the class that accesses the header info (SOAPHeader).

 using System; using System.Web.Services; using system.Web.Services.Protocols; using System.Text.RegularExpressions; public class BirthDayClient {    static void Main(string[] args)    {       SOAPHeaderContent acctInfo = new SOAPHeaderContent();       acctInfo.UserName = "Vincent";       acctInfo.PassWord = "arles";       BirthDay bd = new BirthDay();       bd.SOAPHeaderContentValue = acctInfo;       try {          string dayOfWeek = bd.GetDayBorn(12,20,1963);          Console.WriteLine(dayOfWeek);       } catch (SoapException ex)          {             // Extract Soap error message             // Be sure to add:             // using System.Text.RegularExperssions             Match errMatch = Regex.Match(ex.Message,":(.*)");             Console.WriteLine(errMatch.Groups[1]);          }    } } 

Handling SOAP Exceptions

As shown in Listing 18-5, the Web Service throws an exception if the user name and password cannot be authenticated:

 throw new SoapException("Valid User info not included.",       SoapException.ClientFaultCode); 

The exception is rendered as a SoapException object. In this example, its constructor receives a message and a SOAP fault code that signifies the type of error. This information is returned to the client as a <Fault> element in the SOAP body and is converted by the .NET Framework back to a SoapException object that can be processed by the client's code.

The SoapException object includes four properties that provide information about the exception:

  • Message. The error message that explains the reason for the exception.

  • Actor. The URL of the Web Service that threw the exception.

  • Code. An XMLQualifiedName object that specifies one of four SOAP fault codes that categorize the exception. These fault codes are represented as static fields of the SoapException class.

  • Detail. An XMLNode object containing application-specific information about the error.

The Message and Code properties are used most frequently in processing a SOAP exception. The message is verbose: It consists of the full namespace qualification of the SoapException class followed by the actual message and the name of the Web method where the exception occurred. A regex was used in the preceding client code to extract the actual message.

The SoapException class contains static fields that can be compared with the Code value to broadly classify the exception. These fields include the following:

  • VersionMismatchFaultCode. The SOAP envelope has an invalid namespace.

  • ClientFaultCode. The message sent by the client is incorrectly formatted or contains incorrect information.

  • ServerFaultCode. The error occurs on the server and is not related to the SOAP message. An example would be a network or hardware problem.

  • MustUnderstandFaultCode. A SOAP element marked with the MustUnderstand attribute set to TRue is not processed. This is an attribute that indicates the particular element must be processed.

Here is a code sample that demonstrates using the Code property. It first extracts a message embedded in the Message property and prints it. Then it checks the Code property to classify the excepton.

 try {       string dayOfWeek = bd.GetDayBorn(12,20,1963);       Console.WriteLine(dayOfWeek);     } catch (SoapException ex)     {        Match errMatch = Regex.Match(ex.Message,":(.*)");        Console.WriteLine(errMatch.Groups[1]);        // Check various fault codes here        if(ex.Code == SoapException.ClientFaultCode)        {           Console.WriteLine("Problem with Client message.");        }        if(ex.Code == SoapException.ServerFaultCode)        {           Console.WriteLine("Problem with Server. Try again.");        }     } 

SOAP Security

The preceding example illustrates a lightweight technique for using SOAP headers to authenticate a user. Its main drawback is that the contents of the SOAP headers are passed as cleartext. To overcome this, you can add a layer of encryption by using this technique in conjunction with Secure Sockets Layer (SSL).

As an alternative to this and other ad hoc approaches to SOAP security, there is now a Web Services Security (WS-Security) specification [3] that defines enhancements to SOAP messaging. Its objectives are to ensure the following:

[3] http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf

  • Authentication. How a SOAP message expresses the identity of its sender.

  • Integrity. How to verify that a SOAP message has not been tampered with.

  • Confidentiality. What protects the contents of a SOAP message from being read by an intermediary.

In support of this specification, Microsoft provides an add-on to the .NET Framework known as Web Services Enhancements 2.0 (WSE 2.0). This tool set comprises a .NET class library that can be used to integrate the WS-Security specification into Web Service applications. Note that the use of these enhancements requires .NET on both the client and service provider endpoints, so it's not yet a generic Web Service security solution. See the MSDN Web site, msdn.microsoft.com/webservices/webservices/building/wse/default.aspx, for whitepapers and a free download of the enhancements library

     < Day Day Up > 


    Core C# and  .NET
    Core C# and .NET
    ISBN: 131472275
    EAN: N/A
    Year: 2005
    Pages: 219

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net