Web Services

The computer technology industry is well known for its mastery of the hype life cycle, but rarely has a concept emerged from obscurity to the "new thing" as quickly as Web Services. Vendors large and small are betting that this emerging technology trend will take hold and enable new possibilities for E-business. But what exactly are Web Services?

The first important fact you should know is that Web Services is not a single technology but a group of closely related, emerging technologies that form a distributed computing architecture based on open and ubiquitous Internet standards.

Although on the surface this might sound like the Application Service Provider model, Web Services are very different. Application Service Providers are entire Web-based applications, while Web Services are reusable software components that provide discrete functionality. Also, while Application Service Provider applications are implemented as a closed "black box," Web Services are designed to be inherently extensible.

For an example, look at the current crop of Web-based auction sites such as eBay or uBid. All these sites offer HTML-based auction services that are meant to be used by people. However, imagine that a company has found a need for auction functionality in its own applications. This company will either need to implement the auction functionality itself or redirect customers to one of the auction sites. If these auction sites have a Web Services interface, this company can remotely invoke the needed functionality to build its own applications.

As we saw earlier, Web Services are not implemented in a monolithic way, but represent a collection of several related technologies. From a developer's point of view, Web Services can be understood as specific implementations of an XML-based messaging system built on the following infrastructure components:

  • Web Service Description To be able to interact with a Web Service, a client must be made aware of the semantics of its services. The WSDL is an XML document used to describe the interface to a Web Service in a structured and standardized way.
  • Web Service Wire Formats SOAP is used as the standard wire format for Web Services. Web Services can also use plain HTTP, GET, and HTTP POST, but most are expected to use SOAP and to use HTTP POST as the transport protocol.
  • Web Service Discovery Web Services make their presence, capabilities, and functionality known throughout the process of discovery. The UDDI specification defines an XML schema and an API that enable clients to dynamically find Web Services.

These technologies together, HTTP + XML + SOAP + WSDL + UDDI, form the core low-level technology stack of Web Services.

Other higher-level technologies are being developed for providing strategic aspects of business processes. One of these technologies is the Web Services Flow Language (WSFL) sponsored by IBM. WSFL aims to define a framework whereby Web Services implementers can describe the business logic required to assemble various services into an end-to-end business process.

Describing Web Services with WSDL

WSDL is the IDL of Web Services. WSDL describes the functionality of a Web Service and how this functionality is made available, and plays a central role in enabling Web Service interoperability. It does so by fully describing the capabilities and use of the service in a platform- and protocol-independent manner. The description is so complete that tools can use WSDL to automate the creation of code such as proxies.

WSDL is a W3C-submitted specification and is supported by a number of industry leaders, including Microsoft and IBM. Version 1.1 of the public specification can be found at http://www.w3.org/TR/wsdl.

To illustrate the structure of a WSDL document, let's imagine we have a Web Service, called HelloService, which exposes a single method with the following method signature (shown here in C#):

 String SayHello( String Name ) 

The idea is that the SayHello( ) method, when invoked, will return a string built by concatenating the constant string Hello with the Name parameter passed in the call. So, for example, SayHello( Joe ) will return the string Hello Joe. Listing 7-2, sayhello_request.xml, is a sample SOAP request.

Listing 7-2 sayhello_request.xml: The SOAP message for the SayHello method.

 <?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="http://
schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <SayHello xmlns="http://mspress.microsoft.com/corexml/"> <Name>Joe</Name> </SayHello> </soap:Body> </soap:Envelope>

As you can see, this is just a standard SOAP message using the RPC encoding rules we saw previously when discussing SOAP. Listing 7-3 is a possible SOAP response for our request.

Listing 7-3 sayhello_response.xml: A response message for sayhello_request.xml.

 <?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="http://
schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <SayHelloResponse xmlns="http://mspress.microsoft.com/corexml/"> <SayHelloResult>Hello Joe</SayHelloResult> </SayHelloResponse> </soap:Body> </soap:Envelope>

This response message returns the concatenated string Hello Joe to the SOAP client.

Now we need to talk about describing the service that handled this request and returned our response. To do so we must look at the specifics of the syntax as well as its overall semantics, or structure, in terms of XML. Every WSDL document starts off with the <definitions> root element, and under this are the following five major sections/elements:

  • <types> Defines the data types that the Web Service operates on. The <import> element can be used in place of the <types> element to reference an external schema document.
  • <message> Describes the format of the individual request and response messages. The types of the parameters can either be referenced from those defined in the <types> section or defined directly using the XML Schema default data types.
  • <portType> Defines the request, response messages, or both (defined in the <message> section) that make up the individual methods (called Web methods) in a Web Service.
  • <binding> Specifies the encoding and protocol used by the Web methods defined in the <portType> section.
  • <service> Used to identify the name and physical location of a Web Service—in other words, the endpoint that services call, as well as any bindings the Web Service uses.

All of this probably sounds foreign to you, but hold tight. After we show you a quick example of describing our sample Web Service in WSDL, we're going to spend a few pages discussing each of these items.

To describe our service we will need a file called helloservice.wsdl (in Listing 7-4). This will be the WSDL document for our imaginary HelloService Web Service.

Now that we have an example under our belt, let's look at each of the WSDL elements in more detail.

Listing 7-4 helloservice.wsdl: A sample WSDL document.

 <?xml version="1.0" encoding="utf-8"?> <definitions name="HelloService" xmlns:xsd="http://www.w3.org/2001/XMLSchema"  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://mspress.microsoft.com/corexml/" xmlns="http://schemas.xmlsoap.org/wsdl/"  targetNamespace="http://mspress.microsoft.com/corexml/"> <types> <xsd:schema targetNamespace="http://mspress.microsoft.com/corexml/"> <xsd:element name="SayHello"> <xsd:complexType> <xsd:sequence> <xsd:element name="Name" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="SayHelloResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="SayHelloResult" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </types> <message name="SayHelloSoapIn"> <part name="parameters" element="tns:SayHello"/> </message> <message name="SayHelloSoapOut"> <part name="parameters" element="tns:SayHelloResponse"/> </message> <portType name="HelloServiceSoapPortType"> <operation name="SayHello"> <input message="tns:SayHelloSoapIn"/> <output message="tns:SayHelloSoapOut"/> </operation> </portType> <binding name="HelloServiceSoap" type="tns:HelloServiceSoapPort"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/> <operation name="SayHello"> <soap:operation soapAction="http://mspress.microsoft.com/ corexml/SayHello"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="HelloService"> <port name="HelloServiceSoap" binding="tns:HelloServiceSoap"> <soap:address location="http://localhost/HelloService.asmx"/> </port> </service> </definitions> 

Types

In WSDL, types are defined using XML Schema. The schema can either be embedded within the <types> element, or the <import> element can be used to reference an external schema document. In either case the schema itself will be identical.

In helloservice.wsdl we used XML Schema to define the types we need for the request and response SOAP messages. The following is a snippet of code from that file:

 <types> <xsd:schema targetNamespace="http://mspress.microsoft.com/corexml/"> <xsd:element name="SayHello"> <xsd:complexType> <xsd:sequence> <xsd:element name="Name" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="SayHelloResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="SayHelloResult" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </types> 

This part of our document is standard XML Schema. For the request message we define that we will accept a single parameter called Name, and that it will be an XML Schema xsd:string type. For the response message we define the return value in SayHelloResult, and it is also of XML Schema xsd:string type.

The targetNameSpace, http://mspress.microsoft.com/corexml/, matches the local type namespace (tns) declared for the WSDL. This tells the schema processor that the types defined in this schema are part of that namespace, and we can refer to them later using that namespace prefix.

The <types> element is optional and is meant for defining complex types such as C# structures. For simple types, such as our Name parameter, you can omit this section all together and use XML Schema directly when you need to reference the parameter types in the <message> section. We will show you how when we discuss the <message> section later.

Instead of embedding a schema definition directly in a WSDL document, you can alternatively use the <import> element to reference an external schema. The following shows an example use of the <import> element:

 <definitions name="HelloService" xmlns:xsd="http://www.w3.org/2001/XMLSchema"  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"  xmlns:tns="http://mspress.microsoft.com/corexml/"  xmlns="http://schemas.xmlsoap.org/wsdl/"  targetNamespace="http://mspress.microsoft.com/corexml/"> <import location=http://mspress.microsoft.com/corexml/
HelloService.xsd/> </definitions>

In this example you can see we used the location attribute to specify the actual URL that can be used to import the schema.

Message

A <message> element corresponds to a single piece of information exchanged between the client and the service. A typical request-response method call has two corresponding messages, one for the request and one for the response.

The name attribute is used to uniquely identify a specific message so it can be referenced in the <portType> section later. The subelement <part> is used to define the name and type for a single method parameter or return value. For example, in helloservice.wsdl the following message defines that a message called SayHelloSoapIn has a single parameter of a complex type, called SayHello, which is defined in the <types> section:

 <message name="SayHelloSoapIn"> <part name="parameters" element="tns:SayHello"/> </message> 

The element attribute is used to reference types defined in the <types> section. However, if the type of a parameter can be expressed as an XML Schema datatype, you can use the type attribute instead. The following is an example:

 <message name="SayHelloSoapIn"> <part name="parameters" type="xsd:string"/> </message> 

Here we have declared that our parameter is a simple string type. If all your parameters can be defined using the Schema default data types, you can omit the <types> section altogether.

A WSDL document needs to declare <message> sections for all the possible ways that the Web methods can be invoked, and all the possible responses. For example, if your Web Services support protocols other than SOAP, such as HTTP GET and HTTP PUT, you will need to include those <message> sections as well.

PortType

The <portType> section identifies the set of operations and the messages involved with each of the operations that are exposed by a Web Service. A <portType> completes the interface definition part of a Web Service.

The <operation> section within a <portType> represents the definition of a specific Web method. The name attribute specifies the name of this Web method. Within the <operation> section we then find the <input> and <output> elements that reference the message types defined in the <message> sections to define the request and response messages, respectively. Depending on the presence and ordering of the <input> and <output> elements, four types of operations are possible.

  • <input> A one-way, or write-only, method that accepts a request from a client but does not respond.
  • <input><output> A request-response method that accepts a request from a client and sends this client a reply.
  • <output><input> A solicit-response method that sends a message to the client and expects a reply back from the client.
  • <output> A notification, or read-only, method that sends a message to a client.

For example, in helloservice.wsdl the SayHello Web method is defined as a request-response method.

 <portType name="HelloServiceSoap"> <operation name="SayHello"> <input message="tns:SayHelloSoapIn"/> <output message="tns:SayHelloSoapOut"/> </operation> </portType> 

Binding

The <binding> section is used to specify the protocol details for the various operations defined in the <portType> section and to specify the on-the-wire representation of the messages. The content of the <binding> section is mostly specific to the protocol to which the <portType> is being bound, but every <binding> section will have the following structure:

 <binding name="..." type="..."> <!-- binding details for the portType to be bound --> <operation name="..."> <!-- binding details for this operation --> <input> <!-- binding details for this message --> </input> <output> <!-- binding details for this message --> </output> </operation> </binding> 

Each <binding> element has a name attribute that uniquely identifies the binding. It also has a type attribute that specifies the <portType> element to which this particular binding applies.

First the binding details for the <portType> can be specified. Then you specify the binding parameters for each of the operations defined in the <portType> section specified in the type attribute. Finally you specify the binding details for each of the input messages, output messages, or both defined (in the same order) in the original <portType>. Most Web Services will use SOAP as the wire protocol and, therefore, the SOAP request-response operation is the typical binding type. For example, here is the <binding> section for HelloService, which is a SOAP-based Web Service:

 <binding name="HelloServiceSoap" type="tns:HelloServiceSoapPortType"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/> <operation name="SayHello"> <soap:operation soapAction="http://mspress.microsoft.com/ corexml/SayHello"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> 

The SOAP binding parameters are specified using four basic elements, all of which belong to the http://schemas.xmlsoap.org/wsdl/soap/namespace. The <soap:binding> element is used to specify details about the transport used. The transport attribute specifies the transport protocol (HTTP, SMTP, FTP, and so forth) used and is specified using a standard URI. The style attribute specifies whether the operation specified by this <portType> is RPC- or document-oriented. This element must be placed before any <operation> elements.

For SOAP-over-HTTP binding the <soap:operation> element is used to specify the SOAPAction header of the HTTP request. The <soap:body> and the <soap:header> elements can be used within both the <input> and <output> elements and to specify how the messages they represent appear inside the <Header> and <Body> elements of the SOAP <Envelope> element.

Further details of these and other binding elements for other protocols are beyond the scope of this book, but you can refer to the WSDL specifications at http://msdn.microsoft.com/xml/general/wsdl.asp if you're interested.

Service

The final section, <service>, is used for the collection of bindings that make up a service and also to attach these to physical addresses. This element has a single attribute, name, that is used to uniquely identify the Web Service. One or more <port> elements within it can be used to specify the different bindings and the associated operations that this Web Service supports. The binding attribute specifies the name of the <binding> section to use. Each <port> element will contain elements from the binding-specific namespace, for example, <soap:address>, that are used to specify the addressing details in the form needed by that particular binding.

In helloservice.wsdl our <service> section specifies that we support only one binding, namely HelloServiceSoap, and that the associated operations can be accessed at http://localhost/HelloService.asmx.

 <service name="HelloService"> <port name="HelloServiceSoap" binding="tns:HelloServiceSoap"> <soap:address location="http://localhost/HelloService.asmx"/> </port> </service> 

Although nothing will prevent you from manually writing the WSDL for your Web Services, you should realize that certain tools, such as ASP.NET and the IBM Web Services Toolkit, can help you generate WSDL automatically.

Creating a Web Service

In this section we will show you how you can implement a Web Service and how to invoke methods exposed by the service. We first use the tools provide by the .NET Framework, such as ASP.NET, to implement the service. In later sections, to illustrate the language-independent nature of Web Services, we will show you how you can build consumers, that are not .NET, that can invoke the services provided by the .NET provider.

At the time of this writing the Microsoft .NET Framework SDK is still beta software. The examples here were tested on beta 2 of .NET SDK. You can download the .NET SDK and find information on .NET at the Microsoft MSDN site (http://msdn.microsoft.com/net/).

Although we have more complex examples in this book, we will use ASP.NET to build a simple Web Services provider and consumer. As you will see, ASP.NET makes creating Web Services almost effortless, which should interest all developers.

Our example provides stock quote information over the Web. Of course, we will not be providing actual real-time stock prices and information in this example, that is clearly beyond the scope of this book, but we will provide some hard-coded stock information.

Creating the Provider

Creating a Web Service provider can be summarized as a four-step process. In the first step we will need to create a new source file for the provider. This file will serve as the entry point to our service. The source file will be a plain text file with an .asmx extension (as opposed to an .aspx extension for an ASP.NET Web application). You need to place this file in an Internet Information Server (IIS) virtual directory that has the executescripts permission turned on.

The first line of this file should contain the special ASP.NET directive:

 <%@ webservice %> 

This directive allows you to declare the programming language used to develop this provider and the name of the implementation class. You have the option to include this implementation class in the same file, but that is not a requirement.

Next we will need to instruct our implementation to inherit from the WebService class found in the System.Web.Services namespace. By inheriting from WebService, your provider can gain access to common ASP.NET objects such as Application, Session, User, and Context. If you want your provider to be publicly consumed via the Web, you must specify an XML namespace for your Web Service. This task is accomplished by tagging the implementation class with the appropriate namespace attribute. ASP.NET uses the value of this attribute in the WSDL document and the SOAPAction HTTP header field to uniquely identify the Web-callable endpoints. If you don't specify a namespace attribute, ASP.NET uses a default value of http://tempuri.org/. Finally any methods you want to be accessible via the Web should be tagged with the WebMethod attribute.

The stockservice.asmx file in Listing 7-5 contains the code for our example stock quotes Web Service provider. StockService is designed to return the stock quote information given a stock code. For now, don't worry about how this service can be invoked. We will get to that in a later section when we show you how to implement a consumer for this service.

Listing 7-5 stockservice.asmx: A sample ASP.NET Web Service that returns stock information.

 <%@ webservice language="C#"  %> using System; using System.Collections; using System.Web; using System.Web.Services; [WebService(Namespace="http://mspress.microsoft.com/corexml/")] public class StockService : WebService { /* Hashtable used to store our sample stocks */ private static Hashtable Stocks; public StockService() { StockInfo si; /* the following code populates our Stocks Hashtable with some sample stocks */ Stocks = new Hashtable(); si = new StockInfo(); si.Code = "MSFT"; si.Company = "Microsoft Corporation"; si.Sector = "Technology"; si.Industry = "Software and Programming"; si.Price = 73.00; Stocks.Add( si.Code, si ); si = new StockInfo(); si.Code = "IBM"; si.Company = "Int'l Business Machines"; si.Sector = "Technology"; si.Industry = "Computer Hardware"; si.Price = 113.00; Stocks.Add( si.Code, si ); si = new StockInfo(); si.Code = "GE"; si.Company = "General Electric Company"; si.Sector = "Conglomerates"; si.Industry = "Conglomerates"; si.Price = 48.00; Stocks.Add( si.Code, si ); si = new StockInfo(); si.Code = "SONY"; si.Company = "Sony Corporation"; si.Sector = "Consumer Cyclical"; si.Industry = "Audio & Video Equipment"; si.Price = 65.80; Stocks.Add( si.Code, si ); } /* returns StockInfo object for a given stock code */ [WebMethod(Description="Returns company info and current quote 
price for a given stock code",EnableSession=false)] public StockInfo GetStockInfo( string Code ) { return ( InternalGetStockInfo( Code ) ); } /* returns the quote price for a given stock code */ [WebMethod(Description="Returns current quote price for a given
stock code",EnableSession=false)] public double GetQuotePrice( string Code ) { StockInfo si = InternalGetStockInfo( Code ); if ( si.Code.Equals( Code ) ) { return ( si.Price ); } else { return ( 0.00 ); } } /* Internal method that returns a StockInfo object for a
given stock code. Note that in a real application, you will
probably be calling another service or querying a database
here to obtain the information. */ private StockInfo InternalGetStockInfo( string Code ) { StockInfo si; if ( Stocks.ContainsKey( Code ) ) { si = (StockInfo)Stocks[ Code ]; } else { si = new StockInfo(); si.Code = ""; si.Company = ""; si.Sector = ""; si.Industry = ""; si.Price = 0.00; } return ( si ); } } /* struct used to hold stock information */ public struct StockInfo { public string Code; public string Company; public string Sector; public string Industry; public double Price; }

The first line contains the Web Service ASP.NET directive, where we declare that our provider is written in C# and the implementation class is called StockService.

 <%@ webservice language="C#"  %> 

Next we import the necessary libraries. In our case these are the System, System.Collections, System.Web, and System.Web.Services namespaces. The System.Web and System.Web.Services namespaces reference the ASP.NET and Web Services objects and types. The System.Collections namespace contains the Hashtable class that we're going to use to store the stock information.

We declare our implementation class, StockService, to inherit from the WebService class. We have also tagged our Web Service with an XML namespace called http://mspress.microsoft.com/corexml/. For your Web Services you should substitute this with your personal or your company's XML namespace.

 [WebService(Namespace="http://mspress.microsoft.com/corexml/")] public class StockService : WebService 

For this example we used a Hashtable to store our stock information. The StockService constructor initializes this Hashtable with a few sample stocks. Note that we've used a C# struct for storing the various pieces of data that constitute our stock information. This struct, called StockInfo, is declared at the bottom of the StockService.asmx file.

Methods of an implementation class do not automatically have the ability to be invoked over the Web. These are called Web methods. To define a Web method, apply the WebMethod attribute to any public methods that you want exposed as Web methods. Our Web Service has two Web methods, GetStockInfo( ) and GetQuotePrice( ). GetStockInfo( ) returns a StockInfo struct for a given stock code and GetQuotePrice( ) returns the quote price for a given stock code.

Like many other attributes, the WebMethod attribute supports several optional properties, as shown in Table 7-2.

Table 7-2 WebMethod Attributes

Property Parameter Description

BufferResponse

Boolean

Specifies whether this Web method's output made through the ASP.NET Response object is buffered.

CacheDuration

int

Specifies the time, in seconds, to keep this method's response. Default is not to cache (zero seconds).

Description

string

Provides a description for this Web method. This will be included in the WSDL.

EnableSession

Boolean

Specifies whether you want to use session state for this Web method. The default is true.

MessageName

string

Lets you specify a name for your Web method that is different from that specified in the implementation class. This is usually used for overloaded methods.

Transaction

enumerated (Disabled, NotSupported, Supported, Required, RequiresNew)

Specifies the Transaction mode for this Web method. Default is Disabled.

That's all you need for a simple Web Service! You don't even have to compile the source file. Simply place the StockService.asmx file in an IIS virtual directory and you can access your Web Service from any Web browser. For our example we use the default/root virtual directory, so our Web Service is accessible through the following URL:

 http://localhost/StockService.asmx 

Now that we've built our Web Service provider, let's look at how we can consume it.

Consuming Web Services

One of the many useful features provided automatically for you when you develop Web Services using ASP.NET is a built-in consumer you can use interactively from any browser. Before we show you how you can build your own consumer, it is instructive to take a closer look at this default consumer first.

The Built-In ASP.NET Consumer

To see this built-in consumer in action for yourself, fire up your favorite browser and point it to our StockService.asmx URL. Figure 7-3 shows what you will see when you open this link in Microsoft Internet Explorer.

Figure 7-3 Calling StockService.asmx in Internet Explorer.

This Web page is generated automatically through the use of reflection by ASP.NET with no coding required on your part.

The first page, shown in Figure 7-3, lists all the Web methods you've defined in your Web Service as clickable links. These links will take you to the autogenerated pages that you can use to invoke these methods. In addition, this page also provides a link that generates the WSDL document for your Web Service. Figure 7-4 shows what you will see when you click on the Service Description link.

Figure 7-4 The autogenerated WSDL of StockService.

Like so many features you've seen so far, this WSDL was generated automatically by ASP.NET. You simply write your Web Service and ASP.NET will use reflection to automatically generate the corresponding WSDL document for you.

If you click on one of the Web methods listed in Figure 7-4 you will see something like Figure 7-5 (which shows the page for the GetStockInfo( ) method).

Figure 7-5 Invoking a Web method from Internet Explorer.

You should realize that this page is a true consumer in every right. This consumer, which can be a great tool for testing, uses HTTP GET to communicate with your Web Service provider. It provides an HTML form to let you enter values for the parameters of the Web methods. In the case of our GetStockInfo( ) Web method you can enter a value for the Code parameter. Figure 7-6 shows the resulting Web page when we enter "MSFT" in the Code input box.

Figure 7-6 The result page generated by invoking the GetStockInfo( ) Web method in Internet Explorer.

This is the response when you invoke your Web method using HTTP GET or HTTP POST, which is what this particular consumer uses. A SOAP response will be slightly different.

Going back to the GetStockInfo Web method page shown in Figure 7-5, you see that, in addition to letting you invoke a Web method, you also get useful descriptions of how to access this particular method using SOAP, HTTP GET, and HTTP POST. You also see what their respective responses will look like. For example, this page shows that to invoke the GetStockInfo( ) method via SOAP, you must make the following HTTP transaction:

 POST /StockService.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://mspress.microsoft.com/corexml/GetStockInfo" <?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=
"http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetStockInfo xmlns="http://mspress.microsoft.com/corexml/"> <Code>string</Code> </GetStockInfo> </soap:Body> </soap:Envelope>

As you can see, the built-in ASP.NET consumer is feature rich. However, it is intended for interactive use, not for programmatic access. To be able to access the StockService Web Service programmatically, we need to create our own consumer.

Writing our own consumer might look like a daunting task on the surface, especially when you consider that a truly useful consumer will need to deal with factors like SOAP and WSDL directly. Luckily, ASP.NET comes to our rescue once again. Now let's learn how we can use the features and tools provided by the .NET Framework.

Creating a .NET Consumer

Implementing a consumer using the .NET Framework is surprisingly simple. The task is greatly simplified through the use of proxy classes, which contain built-in functionality to automatically handle all the gory details of invoking XML Web services, such as making HTTP requests and constructing and parsing SOAP messages. The goal is to invoke services as easily as you would invoke local objects. The great feature in .NET is that you don't have to create these proxy classes for your services. .NET provides several tools to help you generate proxy classes to XML Web services automatically; one of them is wsdl.exe.

The following command line shows how we can use wsdl.exe to generate a proxy class for our StockService Web Service. The wsdl.exe application uses the information found in this WSDL document to build a proxy class tailored to your Web Service specifications.

 wsdl /language:cs /protocol:soap http://localhost/StockService.asmx?wsdl 

This command line will generate a proxy class file called StockService.cs. The first parameter, /language:cs, indicates that we want to generate a proxy class written in C#. The second parameter, /protocol:soap, specifies that the generated proxy class will use the SOAP protocol to talk to the StockService WebService. Other valid values are httpget and httppost for the HTTP protocol GET and POST methods. The last parameter specifies the location of the WSDL document for StockService. This can be a URL or a local file specification.

Even though our StockService.cs proxy class is written in C#, by compiling this into a DLL, you can use this proxy from consumers developed in .NET-supported languages. The following command line uses the C# csc.exe compiler to compile the StockService.cs proxy class into a DLL:

 csc /t:library /r:system.web.services.dll StockService.cs 

Running this command line will generate a StockService DLL file that you can reference in your consumer projects. Armed with the StockService proxy class, writing a consumer for our StockService Web Service is almost trivial. Listing 7-6, net_consumer.cs, shows an example of such as a consumer.

Listing 7-6 net_consumer.cs: A .NET consumer of the StockService Web Service.

 using System; public class TestStockService { public static void Main( string[] Args ) { /* make sure the user has specified a stock code */ if ( Args.Length < 1 ) { Console.WriteLine( "usage: teststockservice stock_code" ); return; } /* sc contains the user specified stock code */ string sc = Args[ 0 ]; /* Instantiate the StockService's proxy class */ StockService ss = new StockService (); /* Invoke GetBooks() over SOAP and get the data set. */ StockInfo si = ss.GetStockInfo( sc ); /* Display the results */ Console.Write( "Stock Code: " ); Console.WriteLine( si.Code ); Console.Write( "Company  : " ); Console.WriteLine( si.Company ); Console.Write( "Industry : " ); Console.WriteLine( si.Industry ); Console.Write( "Sector : " ); Console.WriteLine( si.Sector ); Console.Write( "Price  : " ); Console.WriteLine( si.Price ); } } 

NET_Consumer.cs is a simple console application that, given a stock code specified on the command line, will display the respective stock information. See how we access the StockService Web Service by simply using the new operator to instantiate an instance of the StockService class?

 StockService ss = new StockService (); 

The ss variable now holds a reference to StockService, which can now be used to access all the Web methods exposed by the StockService Web Service. For example, in NET_Consumer.cs, to invoke the GetStockInfo( ) Web method we simply invoke the corresponding method in the proxy.

 StockInfo si = ss.GetStockInfo( sc ); 

The rest of NET_Consumer.cs then displays the information returned in the StockInfo struct. To compile NET_Consumer.cs, enter the following command line in a console window:

 csc /r:System.dll /r:StockService.dll NET_Consumer.cs 

We've included a reference to the StockService DLL file we generated earlier. This instructs the compiler to link in the proxy class with NET_Consumer.cs. Figure 7-7 shows a sample output of compiling and running NET_Consumer.cs so you can see what to expect.

You've now seen how easy it is to create a consumer using ASP.NET. But what if you cannot use .NET because your consumer needs to run in an environment that is not .NET?

Figure 7-7 An example output of NET_Consumer.exe.

Creating a Non-.NET Consumer

It would be limiting if Web Services developed using the .NET Framework could be accessed only by a .NET-based consumer. Of course, nothing is further from the truth. You can access .NET-based Web Services from any consumer as long as they adhere to the Web Services specifications. In fact, when it comes to support for SOAP and Web Services, you'll find no shortage of available development toolkits from both commercial and non commercial vendors. To give you a leg up on finding some of these tools, Table 7-3 lists some of the currently available toolkits plus their descriptions and Web sites.

In our example we will use the SOAP Toolkit 2 to develop a consumer for our StockService Web Service. We will also use VBScript as the programming language for implementing this consumer. The code for this consumer can be found in Listing 7-7, soap_toolkit_consumer.vbs.

Listing 7-7 soap_toolkit_consumer.vbs: A sample consumer using the SOAP Toolkit.

 ' instantiate MSSOAP object set SoapClient = CreateObject( "MSSOAP.SoapClient" ) ' setup client proxy from the StockService's WSDL call SoapClient.MSSoapInit( "http://localhost/
StockService.asmx?wsdl" ) ' at this point you can access all the methods exposed by ' StockService through the SoapClient proxy object ' ask user for the stock code code = InputBox( "Enter Stock Code" ) ' invoke the GetQuotePrice method and display the result WScript.Echo "Price = " & SoapClient.GetQuotePrice( UCase( code ) )

Table 7-3 Some SOAP/Web Services Toolkits

Toolkit Language/Platform Web Page URL

Microsoft SOAP Toolkit

Can be used by any COM-compatible languages/systems such as Microsoft Visual Basic, Borland Delphi, and so forth

http://msdn.microsoft.com/library/en-us/soap/htm/soap_overview_3drm.asp

IBM Web Services Too1kit (WSTK)

Java 2

http://www.alphaworks.ibm.com/tech/webservicestoolkit

Apache SOAP

Java 2

http://xml.apache.org/soap/index.html

SOAP:Lite

Perl

http://www.soaplite.com

Web Services for Python

Python

http://sourceforge.net/projects/pywebsvcs/

When soap_toolkit_consumer.vbs is run it will first ask you for the stock code and then display a response. Figure 7-8 shows the message box displayed in a sample response.

Figure 7-8 Running soap_toolkit_consumer.vbs.

As you can see, implementing a basic consumer using the SOAP Toolkit can be extremely easy. You just point it to a WSDL document when you initialize the proxy object through its MSSoapInit method. The proxy automatically loads and interprets the WSDL and builds a COM IDispatch interface.

If, for some remote reason, you can't find a toolkit that suits your needs, you can always roll one of your own. After all, it's just HTTP and XML. As an example, the following ASP application in Listing 7-8 uses the XMLHTTP and DOM objects provided by the MSXML version 2 parser to access the StockService Web Service.

Listing 7-8 asp_consumer.asp: An ASP consumer for the StockService Web Service.

 <% ' retrieve submitted Stock Code value sCode = "" & Request.Form( "pName" ) %> <form method="post" action="asp_consumer.asp"> Enter Stock Code:<br> <input name="pName" value="<%= sCode %>"> <input type="submit" value="Submit"> </form> <hr> <br> <% ' if ( sCode <> "" ) then ' we are going to use the MS XMLHTTP object set oXmlHttp = Server.CreateObject( "MSXML2.XMLHTTP" ) ' open a HTTP POST connection to StockService oXmlHttp.Open "POST", "http://localhost/StockService.asmx", False ' setup HTTP headers. oXmlHttp.SetRequestHeader "Content-Type", "text/xml" oXmlHttp.SetRequestHeader "SOAPAction",  "http://mspress.microsoft.com/corexml/GetStockInfo" ' build string to hold the SOAP request message sSOAPMsg = "<soap:Envelope " & _ " xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""" & _ " xmlns:xsd=""http://www.w3.org/2001/XMLSchema""" & _ " xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" & _ " <soap:Body>" & _ " <GetStockInfo " & " xmlns=""http://mspress.microsoft.com/corexml/"">" & _ " <Code>" & sCode & "</Code>" & _ " </GetStockInfo>" & _ " </soap:Body>" & _ "</soap:Envelope>" ' call StockService oXMLHTTP.Send sSOAPMsg ' retrieve response as a DOM object set oResponseDOM = oXmlHttp.ResponseXML ' extract the StockInfo node values set oNode = oResponseDOM.SelectSingleNode( "//GetStockInfoResult/Code" ) sCode = oNode.Text set oNode = oResponseDOM.SelectSingleNode( "//GetStockInfoResult/Company" ) sCompany = oNode.Text set oNode = oResponseDOM.SelectSingleNode( "//GetStockInfoResult/Industry" ) sIndustry = oNode.Text set oNode = oResponseDOM.SelectSingleNode( "//GetStockInfoResult/Sector" ) sSector = oNode.Text set oNode = oResponseDOM.SelectSingleNode( "//GetStockInfoResult/Price" ) sPrice = oNode.Text end if %> <table border="1"> <tr> <td> Code: </td> <td> <%= sCode %> </td> </tr> <tr> <td> Company: </td> <td> <%= sCompany %> </td> </tr> <tr> <td> Industry: </td> <td> <%= sIndustry %> </td> </tr> <tr> <td> Sector: </td> <td> <%= sSector %> </td> </tr> <tr> <td> Price: </td> <td> <%= sPrice %> </td> </tr> </table> 

ASP_Consumer.asp features an HTML form to let you enter the stock code you want to query and a table that displays the results of invoking the StockService Web Service. Figure 7-9 shows what ASP_Consumer.asp looks like when loaded in a browser.

Figure 7-9 Running ASP_Consumer.asp.

In this example we use the MSXML XMLHTTP object to handle the HTTP POST transactions we need to make to invoke StockService. This task is accomplished in the following line of code:

  oXmlHttp.Open "POST", "http://localhost/StockService.asmx", False 

Next we set up Content-Type and SOAPAction HTTP header directives as required by the SOAP specifications:

 oXmlHttp.SetRequestHeader "Content-Type", "text/xml"
oXmlHttp.SetRequestHeader "SOAPAction",
"http://mspress.microsoft.com/corexml/GetStockInfo"

The only task left to do now to invoke StockService is to build the SOAP <envelope> and send the HTTP POST request.

SOAPAction uses the same XML namespace we specified using the namespace attribute in the StockServer.asmx.

 sSOAPMsg =  "<soap:Envelope " & _  " xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""" & _  " xmlns:xsd=""http://www.w3.org/2001/XMLSchema""" & _  " xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" & _  " <soap:Body>" & _  " <GetStockInfo " &  " xmlns=""http://mspress.microsoft.com/corexml/"">" & _  " <Code>" & sCode & "</Code>" & _  " </GetStockInfo>" & _  " </soap:Body>" & _  "</soap:Envelope>"  ' call StockService  oXMLHTTP.Send sSOAPMsg 

When the call is completed, we retrieve the SOAP response as a DOM object using the XMLHTTP ResponseXML property. Finally we extract the StockInfo field values using XPath and display them in an HTML table.

As you can see, invoking Web Services is not overly complicated, even without special tools. However, this approach is only appropriate for accessing simple Web Services because it can quickly become tedious and unmanageable for even slightly more complicated Web Services.

Discovering Web Services with UDDI

So far we've covered how we can describe, implement and deploy Web Services. But our work is only half done. How will clients find these Web Services on the Internet? Just like people use the yellow pages and search engines to locate Web pages, we need a way for applications and people to locate and discover the capabilities of Web Services. This is where UDDI comes in.

The Specification

The UDDI specification was jointly developed by IBM, Microsoft, and Ariba as an open standard for advertising and discovering Web Services. The UDDI specification defines a Web-based registry framework that exposes information about a business and the services it provide. The framework consists of two parts:

  • An XML Schema (http://www.uddi.org/schema/uddi_1.xsd) that defines data structures that can be used to perform inquiry and publishing functions against any UDDI-compliant registries.
  • A set of SOAP-based APIs that provides a programmatic interface for interacting with any UDDI-compliant registries.

A UDDI Business Registry is an implementation of the UDDI registry framework. Additionally, a UDDI Business Registry provides an HTML-based interface to allow businesses to nonprogrammatically describe themselves and the Web Services they provide and for businesses to locate other potential partners. At the time of this writing, three Web sites provide the UDDI Business Registry service:

  • The Microsoft UDDI Business Registry at http://uddi.microsoft.com
  • The IBM UDDI Business Registry at http://www.ibm.com/services/uddi/
  • The Ariba UDDI Business Registry at http://uddi.ariba.com

As you can see, these are also the same three companies that developed the UDDI specification.

You can find the specifications and other useful information and resources related to UDDI at http://www.uddi.org.

The UDDI Data Model

UDDI organizes the information stored in a UDDI registry into three broad categories. These types of information are represented by three corresponding UDDI data structures. These categories are as follows:

  • White Pages contain simple information about a business such as the name, address, contact, and other known identifiers.
  • Yellow Pages include industrial categorizations based on standard taxonomies as well as descriptive information about the services provided.
  • Green Pages contain the technical information about the services, including references and interfaces to the services.

Figure 7-10 illustrates this data model.

Figure 7-10 UDDI data structures.

These three structures, businessEntities, businessServices, bindingTemplates, represent the complete amount of information provided by the UDDI framework.

The API

The UDDI specification also defines a SOAP-based API that consists of 20 requests and 10 responses, each of which makes use of the data structures outlined previously. The API provides two levels of information. The high-level information returned by the find methods consists of general, summary-level data. The low-level information returned by the get methods contains specific, detail-level data.

For example, the information returned by the find_business UDDI method contains summary information, such as the names and addresses, of businesses that matched the search criteria provided when the method was issued. One of the pieces of information returned is the unique business registration identifier for each of the matching businesses. This identifier can be used with the low-level get_business method to get the entire business registration details.

Table 7-4 lists some of the more commonly used API methods and their corresponding response data structures.

Table 7-4 Common UDDI APIs

Method Purpose Response Data Structure

find_business

Locates businesses based on category, identity, registered URLs, or name.

businessList

find_service

Locates services based on category or name.

serviceList

find_binding

Locates a particular binding within a specific businessService structure.

bindingDetail

find_tModel

Locates summary information about the registered specifications and namespaces.

tModelList

get_businessDetail

Returns the entire detail of a business registration including all the associated businessServices and bindingTemplates.

bindingDetail

get_serviceDetail

Returns the detailed information of a businessService.

serviceDetail

get_bindingDetail

Returns detailed technical binding information for a registered service.

bindingDetail

get_tModelDetail

Returns complete tModel registration information such as the specifications used to create compatible Web Services.

tModelDetail

Let's say, for instance, that we wanted to find the business registration information of Microsoft. The following uddi_find_business.xml in Listing 7-9 is a UDDI request SOAP message containing a find_business method:

Listing 7-9 uddi_find_business.xml: A sample UDDI find_business method call.

 <?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <find_business generic="1.0" xmlns="urn:uddi-org:api"> <name>Microsoft</name> </find_business> </Body> </Envelope> 

As you can see, this is just a normal SOAP request message containing a find_business method with a single parameter called Name. All UDDI messages belong to the urn:uddi-org:api namespace. A sample response of this method is shown in Listing 7-10, uddi_find_business_response.xml.

Listing 7-10 uddi_find_business_response.xml: A sample find_business response.

 <businessList generic="1.0" operator="Microsoft Corporation" truncated="false" xmlns="urn:uddi-org:api"> <businessInfos> <businessInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3"> <name>Microsoft Corporation</name> <description xml:lang="en">Empowering people through great 
software any time, any place and on any device is Microsoft's vision.
As the worldwide leader in software for personal and business computing,
we strive to produce innovative products and services that meet our customers. </description> <serviceInfos> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="1FFE1F71-2AF3-45FB-B788-09AF7FF151A4"> <name>Web services for smart searching</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="8BF2F51F-8ED4-43FE-B665-38D8205D1333"> <name>Electronic Business Integration Services</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="611C5867-384E-4FFD-B49C-28F93A7B4F9B"> <name>Volume Licensing Select Program</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="5DE3CE59-923E-42D3-B7FB-34FC3C3CBC16"> <name>Technet</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="24E553C3-7E3E-484A-8ECA-80E0D0B4A91F"> <name>Microsoft Developer Network</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="77DD86E5-CD70-4219-A28C-37231EAF3901"> <name>Online Shopping</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="0860E130-D4AF-4BD5-9F5C-D7F6FA4B1AD8"> <name>Home Page</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="D2BC296A-723B-4C45-9ED4-494F9E53F1D1"> <name>UDDI Web Services</name> </serviceInfo> <serviceInfo businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3" serviceKey="A8E4999A-21A3-47FA-802E-EE50A88B266F"> <name>UDDI Web Sites</name> </serviceInfo> </serviceInfos> </businessInfo> </businessInfos> </businessList>

For clarity, the response is shown without the SOAP <envelope>. You can see much summary-level information returned by the find_business method, including what services are supported by the matching business entities. Armed with this data, you can now use the get methods to obtain detailed information about a particular business entity or service.

You could use, for example, the following get_serviceDetail method (Listing 7-11), found in uddi_get_servicedetail.xml, to request detailed information for a specific service identified by a serviceKey.

Listing 7-11 uddi_get_servicedetail.xml: A sample get_serviceDetail method call.

 <get_serviceDetail generic='1.0' xmlns='urn:uddi-org:api'> <serviceKey>D2BC296A-723B-4C45-9ED4-494F9E53F1D1</serviceKey> </get_serviceDetail> 

Listing 7-12, uddi_get_servicedetail_response.xml, is the corresponding response.

Listing 7-12 uddi_get_servicedetail_response.xml: A sample get_serviceDetail response.

 <serviceDetail generic="1.0" operator="Microsoft Corporation" truncated="false" xmlns="urn:uddi-org:api"> <businessService businessKey="0076B468-EB27-42E5-AC09-9955CFF462A3"  serviceKey="D2BC296A-723B-4C45-9ED4-494F9E53F1D1"> <name>UDDI Web Services</name> <description xml:lang="en">UDDI SOAP/XML message-based programmatic Web service interfaces.</description> <bindingTemplates> <bindingTemplate bindingKey="313C2BF0-021D-405C-8149-25FD969F7F0B"  serviceKey="D2BC296A-723B-4C45-9ED4-494F9E53F1D1"> <description xml:lang="en">Production UDDI server, Publishing interface</description> <accessPoint URLType="https">https://uddi.microsoft.com/ publish</accessPoint> <tModelInstanceDetails> <tModelInstanceInfo tModelKey="uuid:64C756D1-3374-4E00-AE83-EE12E38FAE63"> <description xml:lang="en">UDDI SOAP Publication Interface </description> </tModelInstanceInfo> </tModelInstanceDetails> </bindingTemplate> <bindingTemplate bindingKey="A9CAFBE4-11C6-4BFE-90F5-595970D3DE24" serviceKey="D2BC296A-723B-4C45-9ED4-494F9E53F1D1"> <description xml:lang="en">Production UDDI server, Inquiry interface</description> <accessPoint URLType="http">http://uddi.microsoft.com/inquire</accessPoint> <tModelInstanceDetails> <tModelInstanceInfo tModelKey="uuid:4CD7E4BC-648B-426D-9936-443EAAC8AE23"> <description xml:lang="en">UDDI SOAP Inquiry Interface </description> </tModelInstanceInfo> </tModelInstanceDetails> </bindingTemplate> <bindingTemplate bindingKey="3FE6C834-293E-4341-AF6E-41DC68949764"  serviceKey="D2BC296A-723B-4C45-9ED4-494F9E53F1D1"> <description xml:lang="en">Test UDDI server, Publishing  interface</description> <accessPoint URLType="https">https://test.uddi.microsoft.com/publish</accessPoint> <tModelInstanceDetails> <tModelInstanceInfo tModelKey="uuid:64C756D1-3374-4E00-AE83-EE12E38FAE63"> <description xml:lang="en">UDDI SOAP Publication  Interface</description> </tModelInstanceInfo> <tModelInstanceInfo tModelKey="uuid:F372E009-F372-429C-A09A-794113A5C5F9"> <description xml:lang="en">urn:microsoft-com:test  signature-element signifies that this is a testing version of the service</description> </tModelInstanceInfo> </tModelInstanceDetails> </bindingTemplate> <bindingTemplate bindingKey="8ED4AD10-C63B-495E-8969-B3938F86E937"  serviceKey="D2BC296A-723B-4C45-9ED4-494F9E53F1D1"> <description xml:lang="en">Test UDDI server, Inquiry  interface</description> <accessPoint URLType="http">http://test.uddi.microsoft.com/inquire</accessPoint> <tModelInstanceDetails> <tModelInstanceInfo tModelKey="uuid:4CD7E4BC-648B-426D-9936-443EAAC8AE23"> <description xml:lang="en">UDDI SOAP Inquiry Interface </description> </tModelInstanceInfo> <tModelInstanceInfo tModelKey="uuid:F372E009-F372-429C-A09A-794113A5C5F9"> <description xml:lang="en">urn:microsoft-com:test  signature-element signifies that this is a testing version of the service</description> </tModelInstanceInfo> </tModelInstanceDetails> </bindingTemplate> </bindingTemplates> <categoryBag> <keyedReference keyName="KEYWORD" keyValue="API" tModelKey="uuid:A035A07C-F362-44DD-8F95-E2B134BF43B4"> </keyedReference> <keyedReference keyName="KEYWORD" keyValue="SOAP" tModelKey="uuid:A035A07C-F362-44DD-8F95-E2B134BF43B4"> </keyedReference> <keyedReference keyName="KEYWORD" keyValue="XML" tModelKey="uuid:A035A07C-F362-44DD-8F95-E2B134BF43B4"> </keyedReference> </categoryBag> </businessService> </serviceDetail> 

We see a lot of detailed information about the service we requested in these examples. The important information for us, though, is the access points that specify the URLs that can be used to invoke the associated Web Services. Finally you can obtain the associated WSDL documents of these Web Services simply by appending a ?wsdl character sequence to the end of any of these URLs.

Unfortunately, further details on the UDDI API are beyond the scope of this book. However, the UDDI Web site (http://www.uddi.org) is an excellent starting place to learn more about this important technology.



XML Programming
XML Programming Bible
ISBN: 0764538292
EAN: 2147483647
Year: 2002
Pages: 134

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