Using SOAP Extensions

Using the WSDL Utility to Generate Proxy Code

The ASP.NET page framework provides a set of classes and tools that greatly simplifies interacting with a Web service. The set of classes provides a base set of functionality for creating Web service proxies. One of the tools is a utility called WSDL.exe that consumes the WSDL for a Web service and then automatically generates proxy code for you.

WSDL.exe ships with the .NET Framework. You can use it to create a strongly typed proxy for accessing the targeted Web service. Just as ASP.NET will map a large number of .NET datatypes to their XML counterparts, WSDL.exe will map XML datatypes described within the Web service's WSDL document to their .NET equivalents.

The functionality of WSDL.exe is integrated within Visual Studio .NET. In Chapter 1, I used the Add Web Reference Wizard to create proxies to interact with the Payment Web service and the WebFileShare Web service. However, the wizard is not as configurable as WSDL.exe. Because some of the more-advanced configuration objects are usually desirable for production applications, the Add Web Reference Wizard is typically used for generating prototyping code.

Recall that the Securities Web service is located at http://woodgrovebank.com/Securities.asmx. The following command will generate a proxy for interacting with the Securities Web service:

wsdl http://woodgrovebank.com/Securities.asmx?wsdl

The command will parse the WSDL document and generate Securities.cs, which contains C# code you can compile to form a strongly typed .NET Securities proxy class that exposes the functionality of the Securities Web service. By default, WSDL.exe will generate C# code that targets the SOAP implementation of the Web service interface.

Like ASP.NET Web services, WSDL.exe can create proxies only for the HTTP protocol. However, WSDL.exe-generated proxies can use one of three bindings: SOAP, HTTP GET, or HTTP POST. You can use optional command-line parameters to set the type of binding as well as other configurations such as the language in which the autogenerated code will be written. Table 6-10 lists the command-line switches that you can specify when you use WSDL.exe to generate a proxy for a Web service.

Table 6-10  Command-Line Switches for WSDL.exe

Switch

Description

/nologo

Suppresses the banner containing the version and copyright information.

/language:[CS VB JS ] or /l:[CS VB JS ]

Specifies the language in which the proxy code should be generated. The default is CS.

/server

Generates an abstract class definition for the Web service itself.

/namespace:[namespace] or /n:[namespace]

Specifies the .NET namespace in which the proxy code will reside.

/out:[filename] or /o:[filename]

Specifies the name of the file that will contain the generated code.

/protocol:[SOAP HttpPost HttpGet]

Specifies the binding the generated proxy code should target. The default is SOAP.

/username:[username] or /u:[username]

/password:[password] or /p:[password]

/domain:[domain] or /d:[domain]

Specifies the credentials that should be passed when connecting to a Web server that requires authentication. The supported authentication types include Basic Authentication and Windows NT Challenge/Response.

/proxy:[url]

The URL of the proxy server. The default is to use the settings defined within the system's Internet Options.

/proxyusername:[username] or /pu:[username]

/proxypassword:[password] or /pp:[password]

/proxydomain:[domain]or /pd:[domain]

Specifies the credentials that should be used to log into the proxy server. The supported authentication types include Basic Authentication and Windows NT Challenge/Response.

/appsettingurlkey:[key] or /urlkey:[key]

Generates code that sets the Url property of the proxy object to the value of the application setting with the specified key in the configuration file. If the application setting is not found, the value will be set to the URL that was originally targeted by WSDL.exe.

/appsettingbaseurl:[url] or /baseurl:[url]

Generates code that sets the Url property of the proxy object to the concatenation of the specified URL and the value of the application setting specified by the /appsettingurlkey switch.

The targeted URL can also point to a DISCO file instead of a WSDL file. A DISCO file provides a simple discovery mechanism and can reference zero or more Web services. I will discuss DISCO files in more detail in Chapter 10.

Targeting a DISCO file provides a convenient way to create proxies for multiple Web services. If WSDL.exe is pointed at a DISCO file, a proxy class will be generated for each Web service referenced by the DISCO file. You can also target multiple Web services by passing multiple URLs as arguments to WSDL.exe.

If multiple proxies are generated, the configuration parameters are applied to all generated proxy classes, which might not be ideal for some parameters. For example, /appsettingurlkey will cause the Url property of every property to be set to the value of the application key. This outcome might not be desirable because in most cases each Web service will resolve to its own unique URL.

If multiple Web services are targeted, they must each reside within their own namespace. If the WSDL document references two schemas with the same namespace (two tempuri.org namespaces, for example), WSDL.exe will generate a warning and create a proxy for only one of the two schemas. To avoid this, you must change the name of one of the namespaces to ensure that they are both unique. The same holds true for duplicate XML Schema namespaces.

If you use WSDL.exe to generate proxy code that will be deployed to production, you should use the following command-line parameters:

  • /language The proxy code should be created using the programming language standardized for the project.

  • /namespace The proxy classes should reside within a namespace to prevent collisions with other datatype definitions.

  • /appsettingurlkey The target URL for the Web service should be stored in the configuration file and not hard coded within the proxy. If the Web service is relocated, you do not need to recompile your code.

Proxy Class

I have discussed how to generate proxy code for accessing the Calculator Web service. Based on the suggestions I made, I will modify the command used to generate the proxy code:

wsdl /language:CS /namespace:BrokerageFirm  /appsettingurlkey:SecuritiesWebServiceUrl  http://woodgrovebank.com/Securities.asmx?wsdl

WSDL.exe will generate Securities.cs. The resulting proxy class, which will be used by a client to access the Securities Web service, is derived from the SoapHttpClientProtocol class. SoapHttpClientProtocol provides the implementation of the proxy. It exposes quite a few properties and methods that control its behavior. Table 6-11 lists the properties, methods, and events for the SoapHttpClientProtocol class in addition to those exposed by the Object and Component classes.

Table 6-11 Selected Properties, Methods, and Events of the SoapHttpClientProtocol Class

Property

Description

AllowAutoRedirect

Specifies whether the proxy will automatically follow redirect requests sent by the server.

ClientCertificates

Specifies a collection of X.509 certificates that can be used to validate the client.

ConnectionGroupName

Specifies the name of the HttpWebRequest connection group to use when connecting to the Web service. A connection group provides a mechanism for allowing multiple clients within the same application to share connections open to a given Web server.

CookieContainer

Used to access the cookies maintained by the proxy. Also provides a mechanism for setting cookies for a particular domain.

Credentials

Specifies authentication credentials that can be used to log into the Web server. The supported methods of authentication include Basic Authentication, Windows NT Challenge/Response, Kerberos, and Digest.

PreAuthenticate

Specifies whether the authentication credentials should be sent immediately or as a result of receiving a 401 access denied error.

Proxy

Contains the information necessary to connect to the proxy server. This includes the URL, port, and user name/domain/password.

RequestEncoding

Specifies the type of encoding that will be used when serializing the request message. The default is UTF-8.

Timeout

Specifies the period of time, in milliseconds, that a synchronous Web request has to complete before the request is aborted. The default is infinity (-1).

Url

Specifies the address of the Web service endpoint.

UserAgent

Specifies the value of the user agent header in the HTTP request.

Method

Description

Abort

Used to abort any asynchronous method calls that are currently executing.

Discover

Used to dynamically discover the location of the Web service via the DISCO file referenced by the Url property.

Event

Description

Disposed

Used to provide notification when the proxy has been disposed.

Let's step through the code generated by WSDL.exe to see how the proxy class is implemented:

//----------------------------------------------------------------------------- // <autogenerated> //     This code was generated by a tool. //     Runtime Version: 1.0.xxxx.xx // //     Changes to this file may cause incorrect behavior and will be lost if  //     the code is regenerated. // </autogenerated> //----------------------------------------------------------------------------- //  // This source code was auto generated by WSDL, Version=1.0.xxxx.xx. //

WSDL.exe first generates comments that document the version of the runtime as well as the version of WSDL.exe that was used to create the proxy. If the proxy will be included within a code base that is released to production, you might also want to record the date and time that the WSDL was generated along with a copy of the WSDL document itself.

The date and time can be recorded by promptly checking the file into a source code repository or by adding it as a comment to the generated file. The WSDL document can be obtained by WSDL.exe itself. You can accomplish this by using one of the optional command-line parameters I discuss later in this section.

namespace BrokerageFirm {     using System.Diagnostics;     using System.Xml.Serialization;     using System;     using System.Web.Services.Protocols;     using System.Web.Services;          /// <remarks/>     [System.ComponentModel.DesignerCategoryAttribute("code")]          [System.Web.Services.WebServiceBindingAttribute(Name="SecuritiesSoap",      Namespace="http://woodgrovebank.com/Securities")]     [System.Xml.Serialization.SoapIncludeAttribute(typeof(SoapReceiptHeader))]     [System.Xml.Serialization.SoapIncludeAttribute(typeof(SoapPaymentHeader))]     public class Securities : System.Web.Services.Protocols.     SoapHttpClientProtocol {                  public SoapPaymentHeader SoapPaymentHeaderValue;                  public SoapReceiptHeader SoapReceiptHeaderValue;

The Securities class is defined within the BrokerageFirm namespace. It is derived from the SoapHttpClientProtocol class. SoapHttpClientProtocol serves as the base class for all ASP.NET proxies and contains the implementation necessary to communicate with most HTTP-based Web services.

The Securities class is also decorated with three attributes. The first is the WebServiceBinding attribute, which serves the exact same role on the client as it does on the Web service. This attribute allows you to formally reference a particular binding defined within another namespace. The two other attributes are SoapInclude attributes. They tell the XML Serializer to include the SoapPayment HeaderValue and the SoapReceiptHeaderValue member variables within the SOAP message.

        /// <remarks/>         public Securities() {             string urlSetting =              System.Configuration.ConfigurationSettings.AppSettings             ["SecuritiesWebServiceUrl"];             if ((urlSetting != null)) {                 this.Url = urlSetting;             }             else {                 this.Url =                  "http://localhost/BrokerageFirm/Securities.asmx";             }         }

The constructor sets the object's Url property to the value of the SecuritiesWebServiceUrl application configuration parameter defined in the application's configuration file. If the value is not found, the Url property is set to the value contained within the HTTP extension element that defines the address of the endpoint within the WSDL document's service definition.

You should consider modifying the else logic to throw an exception instead of defaulting to a hard-coded value. This will make it easier to diagnose some problems within your application. For example, when you are trying to debug your application, it would be easy to overlook the fact that the SecuritiesWebServiceUri parameter is misspelled within your configuration file. (Did you catch the misspelling?)

You might need to dynamically modify the Url property at run time. For example, the application might want to reissue its request to another server in the event of failure. The Url property is a publicly exposed read/write property, so it can be modified by the client at run time.

You can also set the Url property to point to a DISCO file containing a reference to the targeted Web service. You can then call the Discover method to dynamically bind to the Web service contained within the DISCO file. I will cover DISCO files in more detail in Chapter 10.

Within the proxy class definition, methods are defined for each of the operations exposed by the Web service. For each operation, three methods are defined. The first method definition is for synchronously invoking the Web method, and the other two are used in combination to invoke the Web method asynchronously. Here is the synchronous definition for the InstantQuote method:

        [System.Web.Services.Protocols.SoapHeaderAttribute         "SoapReceiptHeaderValue", Direction=System.Web.Services.         Protocols.SoapHeaderDirection.Out)]         [System.Web.Services.Protocols.SoapHeaderAttribute         ("SoapPaymentHeaderValue")]         /// <remarks/>         [System.Web.Services.Protocols.SoapRpcMethodAttribute         ("http://woodgrovebank.com/Securities/InstantQuote",          RequestNamespace="http://woodgrovebank.com/Securities",          ResponseNamespace="http://woodgrovebank.com/Securities")]         public System.Double InstantQuote(string symbol,          CurrencyType targetCurrency) {             object[] results =              this.Invoke("InstantQuote", new object[] {                         symbol, targetCurrency});             return ((System.Double)(results[0]));         }

The InstantQuote method is decorated with the SoapHeader, DebuggerStepThrough, and SoapRpcMethod attributes. The DebuggerStepThrough attribute is used by the Visual Studio .NET debugger. The Visual Studio .NET debugger will not stop within the method marked with this attribute.

The SoapHeader and SoapRpcMethod attributes serve the same purpose as they do when applied to a Web method. The SoapHeader attribute indicates which member variable should be serialized into the header of the SOAP message. The SoapRpcMethod attribute indicates the encoding style and the format of the message as well as the value of the SOAP HTTPAction header.

The signature of the method itself is composed of .NET types that match their XML counterparts described within the types section of the WSDL document. This wrapper method definition allows code written against the proxy to take full advantage of the features provided by the .NET platform. For example, if a client attempts to pass invalid parameters, such as passing two strings to the Add method instead of two integers, the compiler will generate errors at compile time. Developers using Visual Studio .NET will also have full IntelliSense capabilities when they write code against the proxy.

The implementation of the InstantQuote method packages the parameters into an array of objects and calls the Invoke method. Because this method is publicly exposed, you can call it directly. However, using the method exposed by the WSDL.exe-generated proxy provides a more convenient and natural calling convention.

In many circumstances, making a synchronous call to a Web method is not ideal. This is especially true for Web services accessed via the Internet, where quality and speed of the connection might be uncertain. This might also be true for Web services hosted within the walls of a corporate data center. For example, a Web service might be used to expose data contained within a mainframe. A significant amount of initialization might need to be done to set up a connection to the mainframe, or the Web service might be accessed during times of peak load.

The next two methods defined for the InstantQuote operation are BeginInstantQuote and EndInstantQuote. These methods are used to make an asynchronous call to the Securities Web service's InstantQuote Web method:

        /// <remarks/>         public System.IAsyncResult BeginInstantQuote(string symbol,          CurrencyType targetCurrency, System.AsyncCallback callback,          object asyncState) {             return this.BeginInvoke("InstantQuote",              new object[] {symbol, targetCurrency},              callback, asyncState);         }                  /// <remarks/>         public System.Double EndInstantQuote(System.IAsyncResult          asyncResult) {             object[] results = this.EndInvoke(asyncResult);             return ((System.Double)(results[0]));         }     }

By convention, the method used to invoke the asynchronous call is prefixed with Begin and the method used to retrieve the parameters returned by the Web service is prefixed with End. The implementation invokes the BeginInvoke and EndInvoke methods, respectively.

The asynchronous methods are not decorated with attributes used to describe the formatting of the message. The methodName parameter contains the name of the method that the ASP.NET runtime will use to retrieve the formatting information. If the asynchronous message is decorated with any attributes such as SoapDocumentMethod, these attributes will be ignored.

    [System.Xml.Serialization.SoapTypeAttribute("SoapReceiptHeader",      "http://woodgrovebank.com/Securities/encodedTypes")]     public class SoapReceiptHeader : SoapHeader {                  public System.Double Amount;                  public int ReferenceNumber;     }          [System.Xml.Serialization.SoapTypeAttribute("SoapPaymentHeader",      "http://woodgrovebank.com/Securities/encodedTypes")]     public class SoapPaymentHeader : SoapHeader {                  public string NameOnCard;                  public string CreditCardNumber;                  public CardType CreditCardType;                  public System.DateTime ExpirationDate;     }          [System.Xml.Serialization.SoapTypeAttribute("CardType",      "http://woodgrovebank.com/Securities/encodedTypes")]     public enum CardType {                  VISA,                  MC,                  AMX,                  DISCOVER,     }          [System.Xml.Serialization.SoapTypeAttribute("CurrencyType",      "http://woodgrovebank.com/Securities/encodedTypes")]     public enum CurrencyType {                  US_DOLLARS,                  UK_POUNDS,                  GE_DEUTSCHMARKS,     } }

Lastly WSDL.exe defines .NET counterparts to the Payment and Receipt SOAP headers as well as the CurrencyType and CardType enumerations. WSDL.exe uses the SoapType attribute to explicitly define type information used by the XML Serializer to map the .NET types to their XML Schema counterparts.

The use of the proxy to make a synchronous method call is fairly trivial. The following example writes the price of a security passed as a command-line argument out to the console:

using System; using BrokerageFirm; class Application {     public void Main(string[] args)     {         string symbol = args[0];         Securities securities = new Securities();         // Create and initialize the Payment header.         SoapPaymentHeader paymentHeader = new SoapPaymentHeader();         paymentHeader.CreditCardNumber = "12345";         paymentHeader.ExpirationDate = DateTime.Today;         paymentHeader.CreditCardType = CardType.VISA;         securities.SoapPaymentHeaderValue = paymentHeader;         Console.WriteLine("{0} = {1}", symbol,          securities.InstantQuote(symbol, CurrencyType.US_DOLLARS));     } }

Because the Payment header is required to be passed to the InstantQuote method, I create a new SoapPaymentHeader object. Then I initialize it and set it to the SoapPaymentHeaderValue property on the securities object. The proxy is responsible for serializing the SoapPaymentHeader object within the header of the SOAP request message.

Invoking the InstantQuote Web method asynchronously involves a little more work. The following code is contained within a WinForm application. Let's walk through an example. I will write a console application that uses the Securities Web service proxy to make an asynchronous method call:

using System; using System.Web.Services.Protocols; using BrokerageFirm; namespace SecuritiesClient {     class Application     {         static Securities securities = new Securities();

First I create a class that will contain the console application. Then I create a static instance of the Securities Web service proxy as a static member of the class. I do this because the static callback function that I will now define will need to access the proxy object:

        static void Main(string[] args)         {             string symbol = args[0];             SoapPaymentHeader paymentHeader = new SoapPaymentHeader();             paymentHeader.CreditCardNumber = "12345";             paymentHeader.ExpirationDate = DateTime.Today;             paymentHeader.CreditCardType = CardType.VISA;             securities.SoapPaymentHeaderValue = paymentHeader;             securities.BeginInstantQuote(symbol,              CurrencyType.US_DOLLARS,              new AsyncCallback(InstantQuoteCallback), symbol);             System.Threading.Thread.Sleep(30000);             Console.WriteLine("Terminating application.");         }

As you have learned, WSDL.exe will properly handle generating proxies for Web services that support headers. The generated proxy code will contain a class declaration for each header defined by the Web service. Depending on the direction of the header, instances of the header class can be either retrieved or set using an associated property defined by the proxy class for the Web service. By default, the property will have the same name as the class, with a prefix of Value. If the class declaration contains an XmlType attribute (discussed in Chapter 7), the property on the client will simply be the name given to the XML type.

The proxy class will also perform client-side validation of the SOAP headers before sending the message to the server. For example, the proxy will throw a SoapException if SoapPaymentHeaderValue was set to null when the Web method was invoked.

Within the Main function, a call is made to the BeginInstantQuote method. This method accepts two parameters in addition to the securities symbol and the target currency of the quote. I also pass an instance of the AsyncCallback delegate that serves as a reference to the InstantQuoteCallback method I will define shortly. This tells the Web service proxy to execute the InstantQuoteCallback method once the Web service returns. If there is no callback method that should be invoked, you can pass null for the value of the parameter.

The fourth parameter is intended to pass state that should be associated with the method once the callback has been invoked. The parameter is of type object and therefore accepts an instance of any .NET type. In this case, I pass the symbol of the security for which I have requested the quote.

        public static void InstantQuoteCallback(IAsyncResult result)         {             // Obtain the results.             double price = securities.EndInstantQuote(result);             // Obtain the additional state that was sent by              // the call to BeginCallback.             WebClientAsyncResult webResult = (WebClientAsyncResult)result;             string symbol = (string)webResult.AsyncState;             // Display the results within a message box.             Console.WriteLine("{0} = {1}", symbol, price);         }     } }

The InstantQuoteCallback method receives a reference to the IAsyncResult interface of an object of type WebClientAsyncResult. This parameter is then passed to the EndAdd method to obtain the return value of the Web method call. Next I obtain the additional state information from the AsyncState property—in this case, the symbol passed to the Add method. Finally the price of the security is written to the console.

Cookies

Proxies derived from SoapHttpClientProtocol fully support HTTP cookies. However, the proxies have cookies disabled by default. To enable cookie support, you must set the CookieContainer property on the proxy object to reference an instance of a CookieContainer object.

Earlier in the chapter, I leveraged session state to configure the target currency. The client first sets the target currency by calling SetCurrency. Then the client calls InstantQuote to obtain the price of the security. Because the Web service relies on cookies to maintain session state, clients using this Web service need to explicitly enable cookies. The following code demonstrates how to enable session state:

using System; using BrokerageFirm; using System.Net; class Application {     public void Main(string[] args)     {         string symbol = args[0];         Securities securities = new Securities();         // Enable session state by creating a new cookie container.         securities.CookieContainer = new CookieContainer();         // Receive a quote on the requested security in UK pounds.         securities.SetCurrency(CurrencyType.UK_POUNDS);         Console.WriteLine("{0} = {1}", symbol,          securities.InstantQuote(symbol));     } }

Once the proxy object has gone out of scope, all cookie information will be invalid. This is perfectly acceptable in the above console application. However, this might not be ideal if you need to maintain the cookie information across instances of the proxy. In such cases, it is necessary to persist the cookie collection and associate it to the new proxy object.



Building XML Web Services for the Microsoft  .NET Platform
Building XML Web Services for the Microsoft .NET Platform
ISBN: 0735614067
EAN: 2147483647
Year: 2002
Pages: 94
Authors: Scott Short

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