perfectxml.com
 Basic Search  Advanced Search   
Topics Resources Free Library Software XML News About Us
home » free library » Microsoft Press » Chapter 6: ASP.NET from the book Building XML Web Services for the Microsoft® .NET Platform Mon, Aug 13, 2007
Chapter 6: ASP.NET

Web Service Documentation

The ASP.NET runtime includes a set of services that provide documentation for your Web service. The ASP.NET runtime uses reflection to generate two types of documentation: human-readable documentation, and documentation used by client applications to interact with the Web service.

You can reach HTML-based documentation by entering the URL of the Web service into a Web browser. Both the WebService and WebMethod attributes expose a Description property. The following example is a modified version of the Securities Web service I created earlier:

  Sample Chapter from the book:


Building XML Web Services for the Microsoft® .NET Platform
using System;
using System.Web.Services;
 
namespace BrokerageFirm
{
    [WebService(Description="This Web service provides  services 
    related to securities.")]
    public class Securities : WebService
    {
        [WebMethod(Description="Used to obtain a real- time quote 
        for a given security.")]
        public double InstantQuote(string symbol)
        {
            double price = 0;
 
            switch(symbol)
            {
                case "MSFT":
                    price = 197.75;
                    break;
 
                case "SUNW":
                    price = 2.50;
                    break;
 
                case "ORCL":
                    price = 2.25;
                    break;
            }
 
            return price;
        }
    }
}

The HTML-based documentation that is automatically generated for the Securities Web service is shown here:

The Web page lists the method exposed by the Web service, InstantQuote. The Web page also provides a recommendation to set the default namespace of the Web service and provides code examples for C# and Visual Basic .NET. I cover how to set the default namespace later in the chapter.

If you click on a particular method, you see documentation for that particular method, as shown here:

The documentation for the InstantQuote method shows the parameter that is expected to be passed. The text set by the WebMethod attribute’s Description property is also displayed. The Web page also provides an example template for how the parameters should be encoded within SOAP, HTTP GET, and HTTP POST messages.

If the parameters are simple, as in the case of the InstantQuote method, the generated documentation for the method also includes a test harness for posting data via HTTP GET to the Web service. This simple test harness can come in handy for testing the logic within the Web service. It also serves as the default client when you debug your Web service in Visual Studio .NET.

The documentation automatically generated by ASP.NET serves as a good starting point. However, you should consider expanding it to include more descriptive information about the Web service. You should also consider showing a few examples, including the contents of the request and response messagesespecially for Web services that will be publicly exposed via the Internet.

You can configure ASP.NET to display your custom documentation when a user navigates to the .asmx file by setting the wsdlHelpGenerator element within the configuration file. The HTML documentation for the Securities Web service displayed in the preceding graphic is generated by DefaultWsdlHelpGenerator.aspx, which is located in the C:\WINNT\Microsoft.NET\Framework\version\CONFIG directory. The entry within the machine.config file for the default HTML documentation is as follows:

<configuration>
 
  <!--  Portions of the configuration file removed for clarity  -->
  <system.web>
    <webServices>
      <wsdlHelpGenerator href="DefaultWsdlHelpGenerator.aspx" />
    </webServices>
  </system.web>
 
</configuration>

Despite the wsdl prefix, the wsdlHelpGenerator element is used to set the file that will display the HTML-based documentation, not the WSDL documentation. The href attribute specifies the name of the file that will be used to display the documentation. If the filename is fully qualified, it should contain the file path to the document, not the document’s URL.

The ASP.NET runtime also generates documentation about a Web service in a format that can be programmatically consumed by clients. The ASP.NET runtime automatically generates a WSDL document that describes the Web service. You can access this by passing the value wsdl to the .asmx file within the query string. The following is a portion of the WSDL generated for the Securities Web service:

The WSDL document describes the Web service and can be used by the ASP.NET platform to create proxies for calling Web methods. I discuss how to create and use Web service proxies in the section titled "Using the WSDL Utility to Generate Proxy Code" later in this chapter. I discussed WSDL in more detail back in Chapter 5.

The autogeneration of both the HTML and the WSDL documentation is enabled by default within the machine.config file. You can disable autogeneration of documentation for the entire machine by modifying the machine.config file, or you can disable it for an individual Web directory by modifying the web.config file. The following example disables the documentation support:

<configuration>
 
  <!--  Portions of the configuration file removed for clarity  -->
  <system.web>
    <webServices>
        <protocols>
          <remove name="Documentation"/> 
        </protocols>
    </webServices>

  </system.web>
 
</configuration>

Unfortunately, there is no way to disable the HTML and WSDL documentation separately.

Raising Errors

Recall that the InstantQuote Web method exposed by the Securities Web service returns the price of a particular security. However, there is one problem with its current implementation. If the Web method does not recognize a symbol, a price of zero will be returned to the client. This is obviously a problem, especially because the current implementation supports only three symbols. To avoid striking panic in the hearts of investors, we need a way to raise an error stating that an invalid symbol was passed.

An error of type SoapException can be thrown when an error is encountered within the Securities Web service. If a SoapException error is thrown by a Web method, the ASP.NET runtime will serialize the information into a SOAP Fault message that will be sent back to the client.

As you learned in Chapter 3, a well-formed Fault element must contain at least two child elements, faultstring and faultcode. The constructor for the SoapException class takes a minimum of two parameters to set the value of these elements. Once an instance of the SoapException object has been created, the values can be accessed by the Message and Code properties.

The Code property is of type XmlQualifiedName. For convenience, the SoapException class defines static fields for each of the base fault codes defined by the SOAP specification. The following extended version of the Securities Web service throws an exception resulting from an error on the server:

using System;
using System.Web.Services;
using System.Web.Services.Protocols;
 
namespace BrokerageFirm
{
    [WebService(Description="This Web service provides services 
    related to securities.")]
    public class Securities : WebService
    {
        [WebMethod(Description="Used to obtain a real- time quote 
        for a given security.")]
        public double InstantQuote(string symbol)
        {
            double price = 0;
 
            switch(symbol.ToUpper())
            {
                case "MSFT":
                    price = 197.75;
                    break;
 
                case "SUNW":
                    price = 2.50;
                    break;
 
                case "ORCL":
                    price = 2.25;
                    break;
                default:
                    throw new SoapException("Invalid symbol. ", 
                    SoapException.ClientFaultCode);

            }
 
            return price;
        }
    }
}

As I explained in Chapter 3, you can define more-specific fault codes. These developer-defined codes can be appended to the base fault code delimited by a period. The following throws an exception that contains a more-specific fault code:

XmlQualifiedName invalidSymbolsFaultCode = 
    new XmlQualifiedName("Client.InvalidSymbol", 
    "http://schemas.xmlsoap.org/soap/envelope/");
throw new SoapException("Invalid symbol.", invalidSymbo lsFaultCode);

Recall that additional elements can also appear within the Fault element. The SoapException class exposes a number of read-only properties to access this information. Because the properties are read-only, the SoapException class has numerous overloaded constructors that enable the properties to be set. Table 6-3 lists the properties that can be set via an overloaded constructor.

Table 6-3 Properties of the SoapException Class

Property Description
Actor Specifies the value of the faultactor element
Code Specifies the value of the faultcode element
Detail Specifies the contents of the faultdetail element
Message Specifies the value of the faultstring element
InnerException Specifies the value of the inner exception
OtherElements Used to access any other child elements that might be present within the Fault element

Both the Detail and the OtherElements properties can contain an arbitrary hierarchy of data. The Detail property is of type XmlNode and can contain more- detailed information about the Fault element. The OtherElements property contains an array of type XmlNode and is used to contain other child elements that might reside within the Fault elements.

If an exception is thrown by the Web method that is not of type SoapException, the ASP.NET runtime will serialize it into the body of the SOAP Fault element. The faultcode element will be set to Server, and the faultstring element will be set to the output of the ToString method of the exception object. The output usually contains the call stack and other information that would be useful for the Web service developer but not the client. Therefore, ensuring that a client-friendly SoapException class will be thrown from the Web method is recommended.

SOAP Encoding Styles

I mentioned in Chapter 5 that the WSDL SOAP extensibility elements define two different encoding styles, Document and RPC. The RPC style of encoding formats the message as described in the section titled "SOAP Encoding" in Chapter 3 and is intended to support procedure-oriented interaction between the client and the server. The Document style of encoding is intended to support document-oriented messages that will be exchanged between the client and the server. See the section titled "Extensibility Elements" in Chapter 4 for more information.

The encoding style affects the format in which the Web service expects SOAP requests to be encoded by the client and how the response received from the Web service will be encoded. ASP.NET Web services have overwhelmingly been showcased as an easy way to facilitate procedure-based communication between the client and the server. But ironically, Document is the default encoding style.

Recall that the default encoding style is set in the WSDL document via the style attribute of the binding extensibility element. You can set the default encoding style value by decorating the class with the SoapDocumentService or SoapRpcService element. Because the InstantQuote Web method is intended to support procedure-oriented communication between the client and the server, the following example uses the SoapRpcService attribute to set the style to RPC:

using System;
using System.Web.Services;
using System.Web.Services.Protocols;
 
namespace BrokerageFirm
{
    [WebService(Description="This Web service provides  services 
    related to securities.")]
    [SoapRpcService]
    public class Securities : WebService
    {
        [WebMethod(Description="Used to obtain a real- time quote 
        for a given security.")]
        public double InstantQuote(string symbol)
        {
            double price = 0;
 
            // Implementation...
 
            return price;
        }
    }
}

All methods defined by the Securities class, including InstantQuote, will default to RPC-style encoding. You can override this default by decorating a method with the SoapDocumentMethod attribute. On the other hand, if you want to formally state that the default is Document, you can do so using the SoapDocumentService attribute.

The SoapDocumentService attribute exposes three properties that you can set to control how the SOAP documents are formatted. Of the three, the SoapRpcService attribute supports only the RoutingStyle property. Table 6-4 describes these properties.

Table 6-4 Properties of the SoapDocumentService Attribute

Property Description
ParameterStyle Specifies whether the parameters are wrapped in a single element within the body of the SOAP message
RoutingStyle Specifies whether the HTTP SOAPAction header should be populated or left blank
Use Specifies whether the default for the encoding style of the messages is Literal or Encoded

Recall from Chapter 4 that the SOAP extension elements also allow you to specify whether the individual messages within an operation are literal or encoded. Literal means that the message must be formatted exactly as dictated by the schema. Encoded means that the message can be encoded as specified. For RPC-style documents, the use attribute is always set to Encoded.

The WSDL SOAP extension elements do not provide a means of specifying a default for the use attribute. Conveniently, the SoapDocumentService attribute does so via the Use property. The ASP.NET runtime will propagate the value of this property to every WSDL message definition. The Use property can be set to one of three values defined in the SoapBindingUse enumeration: Literal, Encoded, or Default. The default is Literal.

The value of the use attribute for RPC-style documents is encoded. The SoapRpcService attribute does not expose a Use property, so the value cannot be changed.

In Chapter 3, I also mentioned that the SOAPAction HTTP header can be empty if the intent of the SOAP message is conveyed in the HTTP request header entry. As you will see in Chapter 9, Universal Description, Discovery, and Integration (UDDI) messages require the HTTPAction header to be empty because each action is posted to a unique URL that conveys the intent of the request.

You can specify whether the SOAPAction header should be populated by setting the RoutingStyle parameter of the SoapDocumentService or SoapRpcService attribute. The RoutingStyle parameter is of type SoapServiceRoutingStyle and can be set to SoapAction or RequestElement. The default is SoapAction.

You can also use the SoapDocumentService attribute to indicate how parameters should be encoded within the body of the message. The ParameterStyle property can be set to one of three values defined in the SoapParameterStyle enumeration: Bare, Wrapped, or Default.

Wrapped means that the parameters will be wrapped within a parent element. The parent element will have the same name as the Web method. Bare means that the parameter elements will appear as direct children of the SOAP body element. The default is Wrapped. Because RPC-style documents follow the encoding style specified by Section 7 of the SOAP specification, parameters are always wrapped.

The SoapDocumentMethod and the SoapRpcMethod attributes are associated with a particular Web method and can be used to override the defaults set by their Web service counterparts. You can also use them to further define how messages sent and received by a Web method should be encoded. Table 6-5 lists the properties exposed by the SoapDocumentMethod attribute. The SoapRpcMethod attribute supports the same set of properties minus the ParameterStyle and Use properties.

Table 6-5 Properties of the SoapDocumentMethod Attribute

Property Description
Action Specifies the URI placed in the HTTP SOAPAction header
Binding Associates a Web method with a particular binding specified by the WebServiceBinding attribute
OneWay Specifies whether the client will receive a response in association with the request
ParameterStyle Specifies whether the parameters are wrapped in a single element within the body of the SOAP message
RequestElementName Specifies the name of the request element within the body of the SOAP message
RequestNamespace Specifies the namespace URI that contains request element definition
ResponseElementName Specifies the name of the response element within the body of the SOAP message
ResponseNamespace Specifies the namespace URI that contains response element definition
Use Specifies whether the encoding style of the messages is Literal or Encoded

ASP.NET supports two of the four message exchange patterns defined by WSDL, request-response and one-way. So far in this chapter, the examples have all been based on the default message pattern, request-response. Both the SoapDocumentMethod and the SoapRpcMethod attributes expose the OneWay property. When set to true, this property states that no response will be returned to the client when that particular Web method is invoked.

The SoapDocumentMethod and the SoapRpcMethod attributes also allow you to specify the name of the element used to wrap the parameters within the request and response messages. You can set the ResponseElementName and RequestElementName properties to the name assigned to their respective elements.

You can also set the namespace in which the datatype of the request or response element is defined by setting the RequestNamespace or ResponseNamespace property. If this property is not set, the namespace defaults to /encodedTypes relative to the Web service’s namespace.

If the ParameterStyle property is set to Wrapped, the properties used to set the element name and the namespace of the response and request messages will be ignored.

Encoding References

When you pass parameters to a remote service, you need to take into account whether the identity of the parameters will be maintained. In some cases, maintaining the identity of the parameters is extremely important. Consider the following Web service, which registers rock climbers for a competition:

public struct Person
{
    public string Name;
    public int Age;
}
 
public class ClimbingCompetition
{
    [WebMethod]
    public void Register(Person contestant, Person bela y)
    {
        // Implementation...
    }
}

The contestant is the individual who will be climbing. The climber will be attached to a rope in case she falls. The belay is the individual who will be holding the other end of the rope on behalf of the climber. With that information in mind, consider the following example:

ClimbingCompetition competition = new ClimbingCompetiti on();
Person climber = new Person();
 
competition.Register(climber, climber);

The preceding code contains an error: it registers a climber as both the contestant and the belay. Needless to say, the climber better be darn sure she is not going to fall! Unfortunately, the Register method has no way to capture this runtime error because structures are, by default, passed by value. Therefore, two independent copies of climber will be passed to the Register Web method.

I am fairly certain that the sponsors of the competition would want to ensure that every climber is being belayed. However, unless identity is maintained, the Register method will have no idea whether the contestant and the belay are one and the same. I will explain two potential ways of solving this problem.

The first way is to pass a unique identifier with each of the entities. For example, in addition to passing the person’s name and age, you might also want to pass the person’s driver’s license number (assuming that the driver’s license number is guaranteed to be unique). The implementation of the Register method could then check to ensure that the license number of the contestant does not match the license number of the belay.

The second way to solve this problem is to pass the instance of the Person structure by reference. In Chapter 3 you learned how SOAP Encoding specifies the use of the href and id attributes to maintain the identity of a parameter passed by reference. ASP.NET leverages this mechanism to maintain the identities of instances of structures that are passed by reference. Let’s take a look at a modified version of the Register method:

public struct Person
{
    public string Name;
    public int Age;
}
 
public class ClimbingCompetition
{
    [WebMethod]
    [SoapRpcMethod]
    public void Register(ref Person contestant, ref Person belay)
    {
        // Verify that the contestant and the belay
        // are not the same person.
        if(Object.ReferenceEquals(contestant, belay))
        {
            throw new SoapException(
                "The contestant and the belay cannot be the same person.",
                SoapException.ClientFaultCode);
        }
 
        // Implementation...
    }
}

In the preceding example, the Register Web method is decorated with the SoapRpcMethod attribute. This instructs the ASP.NET runtime to serialize the parameters using SOAP Encoding as specified in Section 5 of the SOAP 1.1 specification. In addition, each parameter is decorated with the ref keyword, which indicates that the parameter will be passed by reference. This instructs the ASP.NET runtime to maintain the identity of the instance of the structure passed to the Register method. Note that ASP.NET will maintain identity only for Web methods decorated with the SoapRpcMethod attribute or contained within a class decorated with the SoapRpcService attribute.

Unfortunately, ASP.NET is somewhat inconsistent when handling parameters passed by reference. There is one case in which ASP.NET will not maintain the identity of parameters passed by reference. There is another case in which the identity is maintained, but modifications to the parameters passed by reference are not passed back to the caller. Let’s examine each of these situations separately.

With the first issue, the ASP.NET runtime does not properly maintain the identity of parameters when core value types such as Int32 and String are passed by reference. Consider the following example:

public class Issues
{
    [WebMethod]
    [SoapRpcMethod]
    public void Issue1(ref int x, ref int y)
    {
        x += 10;
        y += 30;
    }
}

Since both x and y are decorated with the ref keyword, their values will be round- tripped to the caller. Therefore, any modifications made to the values of x and y by the Issue1 Web method will be reflected on the client. However, because the identities of the parameters are not maintained, your application could be left in an inconsistent state. Consider the following client code:

Issues issues = new Issues();
int z = 10;
issues.Issue1(ref z, ref z);

This code leverages a proxy class generated by the ASP.NET WSDL.exe command line utility and generates the following SOAP request message:

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

Notice that two distinct copies of the value of z were encoded into the request message. Unfortunately, the Issue1 Web method has no way of knowing that the parameters x and y actually point to the same variable z on the client and therefore will act on x and y independently. If identity was maintained, z would equal 50 as a result of calling Issue1. However, because identity wasn’t maintained, x is set equal to 20 and y is set equal to 40, as shown in the resulting SOAP response message:

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

The ASP.NET-generated proxy will first set z equal to 20 and then set z equal to 40. Therefore the final state of z will be 40 instead of the correct value of 50. One potentialalbeit clumsyworkaround is to wrap the common value type within a structure. The following example demonstrates this technique:

public class Issues
{
    public struct Parameter
    {
        public int Value;
    }
 
    [WebMethod]
    [SoapRpcMethod]
    public void Issue1(ref Parameter x, ref Parameter y )
    {
        x.Value += 10;
        y.Value += 30;
    }
}

Unlike core value types, the ASP.NET runtime will maintain the identity of an instance of a structure that is passed by reference. Unfortunately the ASP.NET runtime will also maintain the identity of an instance of a structure that is passed by value. Therefore, the preceding example will exhibit the same behavior even if the ref keyword is specified. If you do not want the identities of the parameters to be maintained, you can decorate the Web method with the SoapDocumentMethod parameter instead of the SoapRpcMethod parameter.

The final issue is that the ASP.NET runtime will serialize .NET reference types in only the SOAP request message and not the response message. By default, reference types are passed by reference to a method. To achieve this behavior, the client must receive the state of the reference type after the message has returned. However, because the ASP.NET runtime will not serialize parameters containing instances of reference types in the return SOAP response message, the client does not have sufficient information to update its variables accordingly.

If you want parameters containing instances of reference types to be passed by reference, you need to decorate the parameter with the ref keyword. Parameters containing instances of reference types that are decorated with the ref keyword will be serialized in both the SOAP request message and the response message.

Granted, it is helpful to have a means by which to pass data contained in reference types one way across the wire in an effort to reduce the amount of data sent. However, this should be accomplished without overloading the meaning of existing keywords. The meanings of keywords should remain the same whether the code is executed locally or remotely. The companion CD contains sample code for the three issues I describe in this section.


Page 2 of 4

Go to page:               



Sample Chapter from the book:


Building XML Web Services for the Microsoft® .NET Platform
  Contact Us |  | Site Guide | About PerfectXML | Advertise ©2004 perfectxml.com. All rights reserved. | Privacy