Chapter 28. XML Web Services

This chapter begins with a short history of multi-tier architecture and network operating systems, a discussion of the early days of the "network as the computer," and a discussion of where system architecture is heading today. The reason for this diversion is to understand the rationale behind Web services.

The chapter next looks at a sample Web service and walks through the process of making it accessible to the Internet as well as accessing it from a client application — both with the Visual Studio IDE and using command-line tools. From there, the chapter moves on to a key feature of Web services: the Service Repository, Discovery, and Universal Description, Discovery, and Integration (UDDI) features that enable remote programmers to correctly access Web services.

Finally, the chapter delves into more in-depth topics during discussion of the four namespaces found in the .NET Framework class library that deal with Web services and how to utilize them with Visual Basic 2008. Moving on, the chapter covers topics such as security, transactions, and the downsides of any distributed architecture (including any downsides associated with the Web services model), followed by a short discussion of where you go from here and how to get there.

Introduction to Web Services

A Web service is a means of exposing application logic or data via standard protocols such as XML, or, more specifically, SOAP (Simple Object Access Protocol). A Web service comprises one or more functions, packaged together for use in a common framework throughout a network. This idea is illustrated in Figure 28-1, where Web services provide access to information through standard Internet protocols, such as HTTP. By using a Web Services Description Language (WSDL) contract, consumers of the Web service can learn about the structure of the data the Web service provides, as well as all the details about how to actually consume it. A WSDL is a description of the remote interface offered from the Web service.

Figure 28-1

Figure 28.1. Figure 28-1

This simple concept provides for a very wide variety of potential uses by developers of Internet and intranet applications alike, as presented in Figure 28-1. Today, the Web services model is often the heart of the next generation of systems architecture because it is all of the following:

  • Architecturally neutral — Web services do not depend on a proprietary wire format, schema description, or discovery standard.

  • Ubiquitous — Any service that supports the associated Web service standards can support the service.

  • Simple — Creating Web services is easy, quick, and can be free. The data schema is human readable. Any programming language can participate.

  • Interoperable — Because the Web services all conform to the same standards, they can all speak to one another.

In basic terms, a Web service is an object with an XML document describing all of the methods, properties, and events sitting between the code and the caller. Any body of code written in just about any programming language can be described with this XML document, and any application that understands XML (or SOAP) over the assigned protocol (such as HTTP) can access the object. That's because the parameters you type after the function name are passed via XML to the Web service, and because SOAP is an open standard.

Microsoft has put a wrapper around all of the XML schemas that support Web services (including SOAP and WSDL), so they end up looking like .NET or COM objects. The following sections look at how the world views a Web service, and how Microsoft views Web services.

Early Architectural Designs

Understanding the history of the search for a decent remote method invocation (RMI) protocol is imperative to an understanding of why Web services are so important. Each of the RMI systems created before the current Web services model solved a particular set of problems, and you will see how current Web services represent the next stage in the evolution of these cross-platform boundaries to solve the problems that former technologies tried to address.

The Network Angle

Throughout the history of computing, networking operations were largely handled by the operating system. UNIX, the networking host of early computing, featured a body of shell operations that provided remarkable user control over network operations. Personal computing was slower to catch up: Microsoft and Apple software didn't inherently support networking protocols until the mid-1990s. Third-party add-ons by Novell and Banyan were available earlier, but they were only an adjunct to the operating system. The concept of the network being the computer did not fully infiltrate the development community until the expansion of the World Wide Web.

Application Development

Let's break away from networking for a minute and look at how application development evolved until now. Early time-sharing operating systems enabled several people to use the same application with its built-in data. These single-tier systems didn't allow for growth in the system's size, and data redundancy became the standard, with nightly batch jobs to synchronize the data becoming commonplace through the 1970s and early '80s.

Eventually, the opportunity presented by networks became the overriding factor in systems development, and enterprise network developers began offering the loosely termed Object Request Brokers (ORBs) on their systems: Microsoft's Transaction Server (MTS), Common Object Request Broker Architecture (CORBA), and the like. These ORBs enabled the separation of the user interface from the business logic using tightly coupled method pooling. This three-tier architecture brings you to the present in development terms, so let's step back and let networking catch up.

Merging the Two with the Web

The HTTP protocol was born in 1990. There were several other information delivery protocols before, such as Gopher, but HTTP was different because of the extensibility of the related language, HTML, and the flexibility of the transport layer, TCP/IP. Suddenly, movement of many formats of data was possible in a stateless, distributed way. Software as a service was born.

Over the next decade, low-level protocols supported by network systems and the Internet became a staple in applications, with SMTP and FTP providing file and information transfer among distributed servers. Remote procedure calls (RPCs) took things to the next level, but they were platform specific, with UNIX implementations in CORBA and Microsoft's Distributed COM (DCOM) leading the pack.

Enterprise development took a clue from the emerging technologies in wide area network (WAN) networking and personal computing, and development for these large-scale business systems began to mature. As usage of networks grew, developers began to solve problems of scalability, reliability, and adaptability with the traditional flat-format programming model. Multi-tier development began to spread the data, processing, and user interface of applications over several machines connected by local area networks (LANs).

This made applications more scalable and reliable by accommodating growth and providing redundancy. Gradually, vendor compliance and the Java programming language provided adaptability, enabling applications to run in a variety of circumstances on a variety of platforms.

However, there was a dichotomy between the capabilities of the network and the features of the programming environment. Specifically, after the introduction of XML, there still existed no "killer app" using its power. XML is a subset of Standard Generalized Markup Language (SGML), an international standard that describes the relationship between a document's content and its structure. It enables developers to create their own tags for hierarchical data transport in an HTML-like format. With HTTP as a transport and SOAP as a protocol, still needed was an interoperable, ubiquitous, simple, broadly supported system for the execution of business logic throughout the world of Internet application development.

The Foundations of Web Services

The hunt began with a look at the existing protocols. As had been the case for years, the Microsoft versus Sun Alliance debate was heating up among RPC programmers. CORBA versus DCOM was a source of continuing debate for developers using those platforms for distributed object development. After Sun added Remote Method Invocation to Java with Java-RMI, there were three distributed object protocols that fit none of the requirements.

Because DCOM and RMI are manufacturer-specific, it makes sense to start with those. CORBA is centrally managed by the Object Management Group, so it is a special case and should be considered separately.

RMI and DCOM provide distributed object invocation for their respective platforms — extremely important in this era of distributed networks. Both accommodate enterprisewide reuse of existing functionality, which dramatically reduces cost and time-to-market. Both provide encapsulated object methodology, preventing changes made to one set of business logic from affecting another. Finally, similar to ORB-managed objects, maintenance and client weight are reduced by the simple fact that applications using distributed objects are by nature multi-tier.

DCOM

DCOM's best feature is the fact that it is based on COM, one of the most prevalent desktop object models in use today. COM components are shielded from one another, and calls between them are so well defined by the OS-specific languages that there is practically no overhead to the methods. Each COM object is instantiated in its own space, with the necessary security and protocol providers. When an object in one process needs to call an object in another process, COM handles the exchange by intercepting the call and forwarding it through one of the network protocols.

When you use DCOM, all you are doing is making the wire a bit longer. With Windows NT4, Microsoft added the TCP/IP protocol to the COM network architecture and essentially made DCOM Internet-savvy. Aside from the setup on the client and server, the inter-object calls are transparent to the client, and even to the programmer.

Any Microsoft programmer can tell you, though, that DCOM has its problems. First, because there is a customer wire transport function, most firewalls do not allow DCOM calls to get through, even though they are by nature quite benign. There is no way to query DCOM about the methods and properties available, unless you have the opportunity to get the source code or request the remote component locally. In addition, there is no standard data transfer protocol (though that is less of a problem because DCOM is mostly for Microsoft networks).

Remote Method Invocation in Java

RMI is Sun's answer to DCOM. Java relies on a really neat, but very proprietary, protocol called Java Object Serialization, which protects objects marshaled as a stream. The client and server both need to be constructed in Java for this to work, but it further simplifies RMI because Java doesn't care whether the serialization takes place on one machine or across a continent. Similarly to DCOM, RMI enables the object developer to define an interface for remote access to certain methods.

CORBA

CORBA uses the Internet Inter-ORB Protocol to provide remote method invocation. It is remarkably similar to Java Object Serialization in this regard. Because it is only a specification, though, it is supported by a number of languages on diverse operating systems. With CORBA, the ORB does all the work, such as finding the pointer to the parent, instantiating it so that it can receive remote requests, carrying messages back and forth, and disputing arbitration and garbage collecting. The CORBA objects use specially designed sub-ORB objects called basic (or portable) object adapters to communicate with remote ORBs, giving developers more leeway in code reuse.

At first glance, CORBA would seem to be your ace in the hole. Unfortunately, it doesn't actually work that way. CORBA suffers from the same problem web browsers do — poor implementations of the standards — which causes lack of interoperability between ORBs. With IE and Netscape, minor differences in the way pages are displayed is written off as cosmetic. When there is a problem with the CORBA standard, however, it is a real problem. Not only is appearance affected, but also network interactions, as if there were 15 different implementations of HTTP.

The Problems

The principal problem of the DCOM/CORBA/RMI methods is complexity of implementation. The transfer protocol of each is based on manufacturers' standards, generally preventing interoperability. In essence, the left hand has to know what the right hand is doing. This prevents a company using DCOM from communicating with a company using CORBA.

First, there is the problem of wire format. Each of these three methods uses an OS-specific wire format that encompasses information supplied only by the operating system in question. This means two diverse machines cannot usually share information. The benefit is security: Because the client and server can make assumptions about the availability of functionality, data security can be managed with API calls to the operating system.

The second problem is the number of issues associated with describing the format of the protocol. Apart from the actual transport layer, there must be a schema, or layout, for the data that moves back and forth. Each of the three contemporary protocols makes numerous assumptions between the client and server. DCOM, for instance, provides ADO/RDS for data transport, whereas RMI has JDBC. While we can endlessly debate the merits of one over the other, we can at least agree that they don't play well together.

The third problem is knowing where to find broadly available services, even within your own network. We have all faced the problem of having to call up the COM + MMC panel so that we could remember how to spell this component or that method. When the method is resident on a server ten buildings away and you don't have access to the MMC console, the next step is digging through the text documentation, if there is any.

The Other Players

On a path to providing these services, we stumble across a few other technologies. While Java applets and Microsoft's client-side ActiveX technically are not distributed object invocations, they do provide distributed computing and provide important lessons. Fortunately, we can describe both in the same section because they are largely the same, with different operating systems as their backbone.

Applets and client-side ActiveX are both attempts to use the HTTP protocol to send thick clients to the end user. In circumstances where a user can provide a platform previously prepared to maintain a thicker-than-HTML client base to a precompiled binary, the ActiveX and applet protocols pass small applications to the end user, usually running a Web browser. These applications are still managed by their servers, at least loosely, and usually provide custom data transmission, utilizing the power of the client to manage the information distributed, as well as display it.

This concept was taken to the extreme with Distributed Applet-Based Massively Parallel Processing, a strategy that used the power of the Internet to complete processor-intense tasks, such as 3-D rendering or massive economic models, with a small application installed on the user's computer. If you view the Internet as a massive collection of parallel processors, sitting mostly unused, you have the right idea. An example of this type of processing is provided by United Devices (www.ud.com).

In short, HTTP can provide distributed computing. The problem is that the tightly coupled connection between the client and server has to go, given the nature of today's large enterprises. The HTTP angle did show developers that using an industry-recognized transport method solved problem number one, wire format. Using HTTP meant that regardless of the network, the object could communicate. The client still had to know a lot about the service being sent, but the network did not.

The goal? Distributed Object Invocation meets the World Wide Web. The problems are wire format, protocol, and discovery. The solution is a standards-based, loosely coupled method invocation protocol with a huge catalog. Microsoft, IBM, and Ariba set out in 1999 to create just that, and generated the RFC for Web services.

What All the Foundations Missed

You may notice that in reviewing the majority of the earlier services there has been little mention of language. That's because it was a problem overlooked by the foundations. Even RMI failed to recognize that you can't make everyone use the same language, even a great language.

HTTP — A Language-Independent Protocol

What we need is a language-independent protocol that accommodates a standard wire transfer, protocol language, and catalog service. Java with Remote Scripting and ActiveX taught us that HTTP is the wire transfer of choice. Why? What does HTTP do that is so great? First, it is simple. The header added to a communication by HTTP is straightforward enough that power users can type it at a command prompt if they have to. Second, it doesn't require a special data protocol; it just uses ASCII text. Third, HTTP traffic can easily get through firewalls (port 80 is usually open). Finally, it is extensible. Additional headers can be added to the HTTP header for application-specific needs, and any intermediary software can just ignore it.

XML — Cross-Language Data Markup

Now that we have a standard wire transfer protocol that we know works, we need a language and a transport mechanism. Existing languages don't really have data description functions, aside from the data management object models such as ADO. XML fits the bill because it is self-describing. The left hand doesn't need to know what the right hand is doing. An XML file transported over HTTP does not need to know the answering system's network protocol or its data description language. The concepts behind XML are so light and open that everyone can agree to support them. In fact, almost everyone has. XML has become the ASCII of the Web.

XML is important to Web services because it provides a universal format for information to be passed from system to system. We knew that, but Web services actually uses XML as the object invocation layer, changing the input and output to tightly formatted XML, making it platform and language independent.

SOAP — The Transfer You Need

Enter Simple Object Access Protocol (SOAP), which uses HTTP to package essentially one-way messages from service to service in such a way that business logic can interpolate a request/response pair. For your Web page to get an example, you'd make a SOAP request that would look something like this:

POST /Directory HTTP/1.1
Host: Ldap.companyname.com
Content-Type: text/xml; charset="utf-8"
Content-Length: 33
SOAPAction: "Some-URI"vs

<SOAP-ENV:Envelope
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
 <SOAP-ENV:Body>
   <m:FindPerson xmlns:m="Some-URI">
     <NAME>Gates</NAME>
   </m: FindPerson>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This is an HTTP page request, just like one you'd see for an HTML page except that the Content-Type specifies XML, and there is the addition of the SOAPAction header. SOAP has made use of the two most powerful parts of HTTP: content neutrality and extensibility. Here is the response statement from the server:

HTTP/1.1 200 OK
Content-Type: text/xml;
charset="utf-8"
Content-Length: 66
<SOAP-ENV:Envelope
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
  <SOAP-ENV:Body>
    <m:FindPersonResponse xmlns:m="Some-URI">
      <DIRECTORY>Employees
      <PERSON>
         <NAME>Bill Gates</NAME>
         <FUNCTION>Architect
            <TYPE>Web Services</TYPE>
         </FUNCTION>
         <CONTACT>
            <PHONE TYPE=CELL>123-456-7890</PHONE>
            <PHONE TYPE=HOME>555-111-2222</PHONE>
         </CONTACT>
      </PERSON>
      </DIRECTORY>
    </m: FindPersonResponse >
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

SOAP enables you to send the XML files back and forth among remote methods. It is similar to XML-RPC, a protocol developed by Dave Winer in parallel with the SOAP protocol. Both protocols provide similar structures, but the official SOAP protocol is used by Visual Basic and the entire .NET platform.

SOAP is not specific to .NET either. The SOAP Toolkit is another set of tools that Microsoft's Web Services Team provides free of charge. It contains a wonderful WSDL editor, retrofit objects for Windows 2000 and Windows NT4 servers, and more. You can find it at http://msdn.microsoft.com/webservices.

Web Services Description Language

A Web Services Description Language (WSDL) document is a set of definitions that is utilized to describe the interface of any of your Web services. Six elements are defined and used by the SOAP protocol: types, message, portType, binding, port, and service. Essentially adding another layer of abstraction, the purpose of WSDL is to isolate remote method invocations from their wire transport and data definition language. Once again, it is a specification, not a language, so it is much easier to get companies to agree to its use.

Because WSDL is just a set of descriptions in XML, it is not so much a protocol as a grammar. Following is the sample service contract for the HelloWorld Web service you will be building shortly. You can see this file by visiting http://localhost/HelloWorldExample/Service.asmx?WSDL using your Web browser after you create the examples:

<?xml version="1.0" encoding="utf-8" ?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
 xmlns:tns="http://localhost/webservice" xmlns:s="http://www.w3.org/2001/XMLSchema"
 xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
 targetNamespace="http://localhost/webservice"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
   <wsdl:types>
      <s:schema elementFormDefault="qualified"
       targetNamespace="http://localhost/webservice">
         <s:element name="HelloWorld">
            <s:complexType />
         </s:element>
         <s:element name="HelloWorldResponse">
            <s:complexType>
               <s:sequence>
                  <s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult"
                   type="s:string" />
               </s:sequence>
            </s:complexType>
         </s:element>
      </s:schema>
   </wsdl:types>
   <wsdl:message name="HelloWorldSoapIn">
      <wsdl:part name="parameters" element="tns:HelloWorld" />
   </wsdl:message>
   <wsdl:message name="HelloWorldSoapOut">
      <wsdl:part name="parameters" element="tns:HelloWorldResponse" />
   </wsdl:message>
   <wsdl:portType name="WebServiceSoap">
      <wsdl:operation name="HelloWorld">
         <wsdl:input message="tns:HelloWorldSoapIn" />
         <wsdl:output message="tns:HelloWorldSoapOut" />
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name="WebServiceSoap" type="tns:WebServiceSoap">
      <wsdl:documentation>
         <wsi:Claim conformsTo="http://ws-i.org/profiles/basic/1.0"
          xmlns:wsi="http://ws-i.org/schemas/conformanceClaim/" />
      </wsdl:documentation>
      <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
       style="document" />
      <wsdl:operation name="HelloWorld">
         <soap:operation soapAction="http://localhost/webservice/HelloWorld"
          style="document" />
         <wsdl:input>
            <soap:body use="literal" />
         </wsdl:input>
         <wsdl:output>
            <soap:body use="literal" />
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:binding name="WebServiceSoap12" type="tns:WebServiceSoap">
      <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"
       style="document" />
      <wsdl:operation name="HelloWorld">
         <soap12:operation soapAction="http://localhost/webservice/HelloWorld"
          style="document" />
         <wsdl:input>
            <soap12:body use="literal" />
</wsdl:input>
         <wsdl:output>
            <soap12:body use="literal" />
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name="WebService">
      <wsdl:port name="WebServiceSoap" binding="tns:WebServiceSoap">
         <soap:address location="http://localhost:40718/Reuters/WebService.asmx" />
      </wsdl:port>
      <wsdl:port name="WebServiceSoap12" binding="tns:WebServiceSoap12">
         <soap12:address
          location="http://localhost:40718/Reuters/WebService.asmx" />
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

This is what makes it all work. Notice that each of the inputs and outputs of the HelloWorldResponse function is defined as an element in the schema. The .NET Framework uses this to build library files that understand how best to format the outgoing requests, so no matter what operating system develops the WSDL, as long as it is well formed according to the WSDL specification, any type of application (it doesn't necessarily need to be a .NET application) can consume it with a simple SOAP request.

In fact, IIS with the .NET Framework is set up to use the WSDL document in order to provide a great auto-generated user interface for developers and consumers to check out and test Web services. After removing the ?wsdl from the preceding URL, you'll see a very nicely formatted documentation screen for the service. Click the function name and you will get the screen shown in Figure 28-2. This is all dynamically generated based upon the contents of the WSDL document, which is itself dynamically generated by .NET.

Figure 28-2

Figure 28.2. Figure 28-2

The WSDL document can also be expanded in order to define your own descriptions. You can use the Description property of both the WebService and WebMethod attributes to provide more details for this .NET-generated test page for your XML Web services.

Building a Web Service

Building Web services with Visual Studio 2008 is incredibly easy. Microsoft has made it a cakewalk to put together a new Web service application and expose methods off that Web service.

To get started, create an ASP.NET Web Service application. Find this option by clicking File

Building a Web Service

Unlike an ASP.NET Web Application project, Visual Studio creates an .asmx file, rather than an .aspx file. The .asmx file extension is short for Active Server Methods, derived from the fact that it contains methods that will be exposed through the Web service.

By default, Visual Studio creates the Web service using the code-behind model for the Web service page. In addition to the .asmx file, Visual Studio also creates a Service.vb file and places this file in the App_Code folder of the project.

Open the Service.asmx file in Visual Studio. It contains only the WebService page directive, as shown here:

<%@ WebService Language="VB" CodeBehind="~/App_Code/Service.vb"
    Class="Service" %>

You use the @WebService directive instead of the @Page directive. The simple WebService directive has only four possible attributes:

  • Class — This required attribute specifies the class used to define the methods and data types visible to the XML Web Service clients.

  • CodeBehind — Required only when you are working with an XML Web Service file using the code-behind model, this enables you to work with Web services in two separate and more manageable pieces instead of a single file. The CodeBehind attribute takes a string value representing the physical location of the second piece of the Web Service — the class file containing all the Web service logic. In ASP.NET 2.0, it is best to place the code-behind files in the App_Code folder, starting with the default Web Service created by Visual Studio when you initially opened the Web Service project.

  • Debug — This optional attribute takes a setting of either True or False. If the Debug attribute is set to True, then the XML Web Service is compiled with debug symbols in place; setting the value to False ensures that the Web service is compiled without the debug symbols in place.

  • Language — This required attribute specifies the language used for the Web Service.

Instead of focusing on the Service.asmx page, double-click on the Service.vb file to open the file in the document window of Visual Studio. With the Service.vb file in the document window, notice that the single method on the page is decorated with the <WebMethod() > attribute. This attribute (System.Web.Services.WebMethodAttribute) is used to tell ASP.NET to expose this particular method through the Web service.

Directly after the WebServiceBinding attribute, place the WebService attribute in code to define a custom namespace, which the industry recommends you always provide. The value of the namespace can be whatever you see fit; it does not have to be an actual URL, just a unique identifier:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace:="http://localhost/HelloWorldExample")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class Service
     Inherits System.Web.Services.WebService

    <WebMethod()> _
    Public Function HelloWorld() As String
        Return "Hello World"
    End Function

End Class

Now add a new method called GoodbyeWorld, without a WebMethod attribute:

Public Function GoodbyeWorld() As String
  Return "Goodbye World"
End Function

Run the project. Visual Studio will open the Service.asmx file. By default, Web services display a test interface (see Figure 28-3) that enables you to see which methods are available and to execute them.

Figure 28-3

Figure 28.3. Figure 28-3

Notice that only the HelloWorld method is displayed. This is the only method decorated with the WebMethod attribute, and hence the reason why GoodbyeWorld and all of the inherited methods on the Service class were not displayed. Clicking the link enables you to invoke the method, as shown in Figure 28-4.

Figure 28-4

Figure 28.4. Figure 28-4

If you do this, the URL http://localhost:#####/HelloWorldExample/Service.asmx/HelloWorld is requested, which happens to be the URL for this specific method (running with the built-in web server provided with Visual Studio). You will then see the payload of the SOAP document directly in the browser, which contains the results of the call, as shown in Figure 28-5.

Figure 28-5

Figure 28.5. Figure 28-5

That is pretty much all there is to Web services from an implementation perspective when working with the .NET Framework. The .NET Framework deals with all of the plumbing (SOAP, WSDL, and so on) discussed in the first part of this chapter on your behalf, so you only have to add properly decorated methods to the service.

A Realistic Example

Although the previous example was very easy to implement, it does not demonstrate a real-world application of Web services. Let's take a look at a more realistic example by building a Web service that provides a richer set of data from a database instead. For this example, imagine that a third-party provider hosts the site. The SQL server is behind a firewall, and the IIS server is in a demilitarized zone — a safe, though exposed, network position, as shown in Figure 28-6.

Figure 28-6

Figure 28.6. Figure 28-6

To get the data from your site to the remote site, you need to call a Web service on the remote web server from your intranet. The SOAP envelope is sent via HTTP, so the firewall allows it to pass through, and ADO.NET on the IIS server handles the actual database manipulation. The remote firewall allows database calls only from the IIS server, and the data is updated safely because of the security.

In real life, the method GetEmployees would be local to your intranet server, and the database file would be a SQL server on a second server. Across the Internet, as shown in Figure 28-6, the Web service would be on an IIS server sitting outside the network firewall. The DLL that actually provides the data functions would be on an application server inside the firewall, and the database would again be on a separate server.

For this application, though, you need to create a Web service that exposes some of the Contact table, as well as some of the Employee table, from the sample AdventureWorks database, across the intranet, which will later be consumed by a Web application. Keep in mind that Web services not only expose simple values, but also a richer data set of values, such as entire tables from a data store (for example, SQL Server).

Start this example by first creating a new Web Service project in Visual Studio called WebService1.

Using Visual Studio 2008 to Build Web Services

The Visual Studio 2008 IDE shows a marked improvement from the add-ins provided for Visual Studio 6 in the SOAP Toolkit. For instance, Web services are shown as references on a project, rather than in a separate dialog box. The discovery process, discussed later, is used to its fullest, providing much more information to the developer. In short, it is nearly as easy to consume a Web service with Visual Basic as it is to use DLLs.

Producing a Typed DataSet

For simplicity, you will use Visual Studio to first create a typed DataSet, which will be returned from the WebMethod that you later produce. This IDE enables you to quickly and easily create the needed data access without having to dig through a lot of ADO.NET code.

Right-click the WebService1 project in the Solution Explorer and select Add New Item. From the provided menu of file options, select DataSet. Change the name of this file to MyDataComponent.xsd. This creates an already strongly typed DataSet on the fly. In addition, Visual Studio requests to place this file in the App_Code folder of your solution. Confirm this request, because having it in the App_Code folder allows for programmatic access to the DataSet (see Figure 28-7).

Figure 28-7

Figure 28.7. Figure 28-7

Once created, the MyDataComponent.xsd file opens itself in Visual Studio. This file appears as a blue screen in the document window. The first step is to drag and drop a single TableAdapter onto this design surface. Once you do this, the TableAdapter Configuration Wizard opens, as shown in Figure 28-8.

Figure 28-8

Figure 28.8. Figure 28-8

The first step in the TableAdapter Configuration Wizard is establishing a data connection. If a connection is not already in place, then create one by clicking the New Connection button. Using this dialog, make a new connection to the sample AdventureWorks_Data.mdf database, which is a SQL Server Express Edition database file. You can find this and other SQL Server 2005 samples online on the Microsoft CodePlex website (www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=4004). Download the file AdventureWorksDB.msi.

Once the connection is defined in the TableAdapter Configuration Wizard, the next step of the wizard asks you to store the connection in the web.config file, which is always a good option to choose. Choosing both of these actions will copy the AdventureWorks_Data.mdf database file to the App_Data folder in your project, and the connection to this database file will now be named and placed within the web.config file of your ASP.NET Web Service project.

<connectionStrings>
   <add name="AdventureWorks_DataConnectionString"
    connectionString="Data Source=.SQLEXPRESS;AttachDbFilename=|DataDirectory|
       AdventureWorks_Data.mdf;Integrated Security=True;Connect Timeout=30;
       User Instance=True"
    providerName="System.Data.SqlClient" />
</connectionStrings>

Once the connection to the data store is established, click Next. In the dialog that appears, pick the command type that you want to work with. Typically, the options are working with either direct SQL commands, existing stored procedures, or stored procedures that you can create directly in the wizard. For this example, choose the first option: Use SQL Statements.

The next page in the wizard asks for the query that you want to use to load the table data. Input the following:

SELECT HumanResources.Employee.EmployeeID, HumanResources.Employee.Title,
    HumanResources.Employee.Gender,
HumanResources.Employee.HireDate, Person.Contact.Title AS EXPR1,
    Person.Contact.FirstName, Person.Contact.MiddleName,
    Person.Contact.LastName, Person.Contact.EmailAddress, Person.Contact.Phone
FROM Person.Contact INNER JOIN
    HumanResources.Employee ON Person.Contact.ContactID =
       HumanResources.Employee.ContactID

Clicking the Next button results in a page from which you can select the methods that the wizard will generate (as shown in Figure 28-9). These are the methods used in your Web service to load data into data sets for transmission. In this case, the Fill and GetData methods are specified with the first two options in the dialog. In some cases, you might want to also select the last check box, which creates the additional Insert, Update, and Delete methods that you might want to later expose via a Web service. When you are done, click Next again to proceed to the next step in the wizard.

Figure 28-9

Figure 28.9. Figure 28-9

Figure 28-10 shows the last page of the wizard. This final page just shows the results of all the actions taken in the preceding steps.

After clicking the Finish button, note that the design surface of the MyDataComponent.xsd file changes to reflect the data that comes from the two tables of the AdventureWorks database (see Figure 28-11).

At this point, your typed data set is now in place and ready for use by the Web service. Looking at the results on the design surface of the .xsd file, you can see that indeed the typed MyDataComponent data set is in place and contains a single DataTable called DataTable1. There is also a DataTable1TableAdapter object with Fill and GetData methods in place.

Figure 28-10

Figure 28.10. Figure 28-10

Figure 28-11

Figure 28.11. Figure 28-11

Building the Service

Right-click Service.asmx from within the Solution Explorer in Visual Studio and select View Code. Rename the HelloWorld function to GetEmployees. From here, simply retrieve data from the DataTable1TableAdapter that was created when you created the .xsd file earlier:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace:="http://www.lipperweb.com/namespace")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class Service
    Inherits System.Web.Services.WebService

    <WebMethod()> _
    Public Function GetEmployees() As MyDataComponent.DataTable1DataTable
        Dim da As New MyDataComponentTableAdapters.DataTable1TableAdapter
        Dim ds As New MyDataComponent.DataTable1DataTable

        da.Fill(ds)

        Return ds
    End Function
End Class

Note

If you are having trouble getting the MyDataComponent object recognized by Visual Studio, before adding this code, be sure to build your application, and then you will find that it is recognized.

Right-click the Service.asmx file in the Solution Explorer and select View in Browser. If there are no errors, then a simple screen listing GetEmployees as the sole method of the service appears. Click the Service Description line. You will get a screen like the one shown earlier in Figure 28-2.

Consuming the Service

Although the Web service is in place, you really have seen only half the story. Exposing data and logic as SOAP to disparate systems across the enterprise or across the world is a simple task using .NET, and particularly ASP.NET. The other half of the story is the actual consumption of an XML Web Service into another application.

Keep in mind that you are not limited to consuming Web services only into ASP.NET applications, as shown shortly. Consuming Web services into other types of applications is not that difficult; in fact, it is rather similar to how you would consume them using ASP.NET. Remember that the Web services you come across can be consumed in Window Forms, Windows Presentation Foundation applications, mobile applications, other databases, and more. You can even consume Web services with other Web services, resulting in a single Web service made up of what is basically an aggregate of other Web services.

For this consuming application, provide a Web application called WSEmployees by creating a new ASP.NET Web Site project with that name. For this example, create this new project within your current solution: Right-click on the WebService1 solution and select Add

Consuming the Service

Adding a Web Reference

The only bit of magic here is the adding of a Web reference to the project using the Visual Studio IDE. As described later, you are really creating a proxy based upon the WSDL file of the service and referencing the proxy in the project, but the IDE makes this all quite simple.

To create the proxy needed by the consuming application, right-click the WSCustomers project in the Solution Explorer and select Add Web Reference from the list of options. In this form, enter the WSDL file of the Web service to which you want to make a reference. If the Web service is a .NET Web Service (with an .asmx file extension), simply input the URL of the .asmx file and nothing more because the wizard automatically adds ?wsdl at the end of the input. If you are referencing a Java Web Service, then place the URL for the .wsdl file in this wizard. In most cases, you would simply enter the URL of the service you are interested in consuming in the address bar of the Add Web Reference dialog. For this example, click the "Web Services found at this URL" link. The dialog box shown in Figure 28-12 should appear.

Figure 28-12

Figure 28.12. Figure 28-12

The service description page you see when you build your service appears in the left pane of the wizard, with .NET-specific information in the right. Click the Add Reference button at the right of the window to add this reference to your project. The service appears in a new folder called Web References within the Solution Explorer, as shown in Figure 28-13.

Figure 28-13

Figure 28.13. Figure 28-13

In making the reference, you can see that the .wsdl file was copied over, as well as the typed DataSet, MyDataComponent. The power of this Web services model is that it can work with the GetEmployees method as if it were now local to your machine, when in fact it is hosted in an entirely different application.

Building the Consumer

The COM architecture continually promised "one line of code" to generate great results. Web services live up to the promise, minus the declarations. Now the only thing left to do is call the referenced Web service and pass the generated DataTable that comes from the DataSet. Compared to the scores of lines of XML needed to pass the DataSet in the existing Microsoft technologies, this is a breeze.

The first step to consuming an .aspx page is simply to make a reference to the proxy that Visual Studio created and then call the GetEmployees WebMethod through this instantiated object. The results pulled from the GetEmployees method are then displayed in a GridView control, which is placed on a Web form.

You have a couple of ways to achieve this. The first method is to use an ObjectDataSource control, which does the work of invoking the GetEmployees WebMethod and then displaying the results in the GridView control. (The second method, discussed a bit later, is to manually write the required code.) To work through this example, drop a GridView and an ObjectDataSource server control onto the design surface of the Web form. Open the smart tag of the ObjectDataSource control and select Configure Data Source. You are then presented with the Configure Data Source Wizard.

In the first page of this wizard, uncheck the Show Only Data Components check box and select localhost.Service from the drop-down list. Click the Next button to choose the SELECT method for that ObjectDataSource control to use (shown in Figure 28-14).

Figure 28-14

Figure 28.14. Figure 28-14

From the drop-down list on this page of the wizard, select GetEmployees(), returns DataTable1DataTable and click Finish to progress to the next step of binding the GridView control to the returned DataTable from this ObjectDataSource control.

Now turn your attention to the GridView control. In configuring this control, open the control's smart tag. From the drop-down list, select ObjectDataSource1 as the data source control for this control. Note that once you do this, the GridView control expands to include all the appropriate columns from the result set you specified earlier from the AdventureWorks database.

Now, in the same smart tag, enable paging by selecting the appropriate check boxes. The code generated by Visual Studio is shown here:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb"
    Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Consuming Application</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" runat="server" AllowPaging="True"
         AutoGenerateColumns="False"
         DataKeyNames="EmployeeID" DataSourceID="ObjectDataSource1">
            <Columns>
                <asp:BoundField DataField="EmployeeID" HeaderText="EmployeeID"
                 InsertVisible="False"
                 ReadOnly="True" SortExpression="EmployeeID" />
                <asp:BoundField DataField="Title" HeaderText="Title"
                 SortExpression="Title" />
                <asp:BoundField DataField="Gender" HeaderText="Gender"
                 SortExpression="Gender" />
                <asp:BoundField DataField="HireDate" HeaderText="HireDate"
                 SortExpression="HireDate" />
                <asp:BoundField DataField="EXPR1" HeaderText="EXPR1"
                 SortExpression="EXPR1" />
                <asp:BoundField DataField="FirstName" HeaderText="FirstName"
                 SortExpression="FirstName" />
                <asp:BoundField DataField="MiddleName" HeaderText="MiddleName"
                 SortExpression="MiddleName" />
                <asp:BoundField DataField="LastName" HeaderText="LastName"
                 SortExpression="LastName" />
                <asp:BoundField DataField="EmailAddress" HeaderText="EmailAddress"
                 SortExpression="EmailAddress" />
                <asp:BoundField DataField="Phone" HeaderText="Phone"
                 SortExpression="Phone" />
            </Columns>
        </asp:GridView>
        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
         SelectMethod="GetEmployees"
TypeName="localhost.Service"></asp:ObjectDataSource>
    </div>
    </form>
</body>
</html>

Once the page is complete, build and run it. That's it. There is now a table in the Web form with all the data from a remote SQL Server Express Edition file that can be paged — and you did not have to write any code to achieve this functionality! The page results are shown in Figure 28-15.

Figure 28-15

Figure 28.15. Figure 28-15

Now consider doing the same thing but instead spending a little time writing some code. This is a good exercise because it offers more control over the situation (if desired), and it teaches you more about what is going on.

Create a page that includes only a GridView server control. From here, you get at the data that comes from the Web service in the Page_Load event, as shown in the following example:

<%@ Page Language="VB" %>

<script runat="server">
    Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim ws As New localhost.Service

        GridView1.DataSource = ws.GetEmployees()
        GridView1.DataBind()
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Consuming Application</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" Runat="server"
         AllowPaging="True" AllowSorting="True">
        </asp:GridView>
    </div>
    </form>
</body>
</html>

The first line of code contained in the Page_Load event instantiates the proxy object that was created for you. The next line assigns the DataSource property of the GridView server control to the result set from the GetEmployees WebMethod call. Finally, you close everything by calling the DataBind method of the GridView control. By compiling and running the XML Web Service, you can retrieve from the database the view of the Employees table that you created earlier from the AdventureWorks database. A returned dataset contains a wealth of information, including the following:

  • An XSD definition of the XML contained in the DataSet

  • The employee information from the Employees table of the AdventureWorks database

On the consumption side, consumers of this XML Web Service can easily use the XSD definition and the XML contained within the DataTable within their own applications. If users are then consuming this DataTable into .NET applications, they can easily bind this data to a GridView and use it within their applications with minimal lines of code.

Overloading WebMethods

In the object-oriented world of .NET, it is quite possible to use method overloading in the code you develop. A true object-oriented language has support for polymorphism, of which method overloading is a part. Method overloading enables you to have multiple methods that use the same name but have different signatures. With method overloading, one method can be called, but the call is routed to the appropriate method based on the full signature of the request. An example of standard method overloading is illustrated in the following code listing:

Public Function HelloWorld() As String
   Return "Hello"
End Function

Public Function HelloWorld(ByVal FirstName As String) As String
   Return "Hello " & FirstName
End Function

In this example, both methods have the same name, HelloWorld. Which one is called when you invoke HelloWorld depends on the signature you pass to the method. For instance, you might provide the following:

Label1.Text = HelloWorld()

This yields a result of just Hello. However, you might invoke the HelloWorld method using the following signature:

Label1.Text = HelloWorld("Bill Evjen")

This returns a result of Hello Bill Evjen. As you can see, method overloading is a great feature that can be effectively utilized by your ASP.NET applications — but how do you go about overloading WebMethods?

If you have already tried to overload any of your WebMethods, you probably got the following error when you pulled up the Web service in the browser:

Both System.String HelloWorld(System.String) and System.String HelloWorld() use the
message name 'HelloWorld'. Use the MessageName property of the WebMethod custom
attribute to specify unique message names for the methods.

As this error shows, the extra step you have to take to overload WebMethods is to use the MessageName property. The following bit of code shows how:

<WebMethod(MessageName:="HelloWorld")> _
Public Function HelloWorld() As String
   Return "Hello"
End Function

<WebMethod(MessageName:="HelloWorldWithFirstName")> _
Public Function HelloWorld(ByVal FirstName As String) As String
   Return "Hello " & FirstName
End Function

In addition to adding the MessageName property of the WebMethod attribute, you have to disable your Web service's adherence to the WS-I Basic Profile 1.0 specification — that it would not be doing if you perform WebMethod overloading with your Web services. You can disable conformance to the WS-I Basic Profile specification in a couple of ways. The first way is to add the <WebServiceBinding> attribute to your code, as illustrated here:

<WebServiceBinding(ConformsTo := WsiProfiles.None)> _
Public Class MyOverloadingExample
   ' Code here
End Class

The other option is to turn off the WS-I Basic Profile 1.0 capability in the web.config file:

<configuration>
  <system.web>
    <webServices>
      <conformanceWarnings>
        <remove name="BasicProfile1_1" />
      </conformanceWarnings>
    </webServices>
  </system.web>
</configuration>

After you have enabled your Web service to overload WebMethods, you can see both WebMethods defined by their MessageName value properties when you pull up the Web service's interface test page in the browser (see Figure 28-16).

Figure 28-16

Figure 28.16. Figure 28-16

Although the names of the WebMethods are distinct (based on the MessageName property values you assigned in your code through the Web service's test page), when the developer consuming the Web service makes a Web reference to your Web service, he or she sees only a single method name available (in this example, HelloWorld). This is evident in the IntelliSense of Visual Studio 2008 in the application consuming these methods (see Figure 28-17).

Figure 28-17

Figure 28.17. Figure 28-17

In the yellow box that pops up to guide developers on the signature structure, two options are available — one is an empty signature and the other requires a single string.

Caching Web Service Responses

Caching is an important feature in almost every application that you build with .NET. Although many features in the .NET Framework provide different vehicles for caching, a feature of Web services in .NET enables you to cache the SOAP response sent to any of the service's consumers.

First, by way of review, remember that caching is the capability to maintain an in-memory store where data, objects, and various items are stored for reuse. This feature increases the responsiveness of the applications you build and manage. Sometimes, returning cached results can greatly affect performance.

XML Web Services use an attribute to control caching of SOAP responses — the CacheDuration property. The following bit of code shows its use:

<WebMethod(CacheDuration:=60)> _
Public Function GetServerTime() As String
   Return DateTime.Now.ToLongTimeString()
End Function

As you can see, CacheDuration is used within the WebMethod attribute much like the Description and Name properties. CacheDuration takes an Integer value that is equal to the number of seconds during which the SOAP response is cached.

When the first request comes in, the SOAP response is cached by the server, and the consumer gets the same time stamp in the SOAP response for the next minute. After that minute is up, the stored cache is discarded, and a new response is generated and stored in the cache again for servicing all other requests for the next minute.

Among the many benefits of caching your SOAP responses, you will find that your application's performance is greatly improved when you have a response that is repeatedly recreated without any change.

SOAP Headers

One of the more common forms of extending the capabilities of SOAP messages is to add metadata of the request to the SOAP message itself. The metadata is usually added to a section of the SOAP envelope called the SOAP header. Figure 28-18 shows the structure of a SOAP message.

Figure 28-18

Figure 28.18. Figure 28-18

The entire SOAP message is referred to as a SOAP envelope. Contained within the SOAP message is the SOAP body — a piece of the SOAP message that you have been working with in every example thus far. It is a required element of the SOAP message.

The one optional component of the SOAP message is the SOAP header, which is the part of the SOAP message where you can place any metadata about the overall SOAP request instead of incorporating it into the signature of any of your WebMethods. It is important to keep metadata separate from the actual request.

It terms of the information it contains, it could include a lot of things. One of the more common items placed in the SOAP header is any authentication/authorization functionality required to consume your Web service or to get at specific pieces of logic or data. Usernames and passwords are good examples of what you might include inside the SOAP headers of your messages.

Building a Web Service with SOAP Headers

You can build upon the sample HelloWorld Web service presented in the default .asmx page when it is first pulled up in Visual Studio. Name the new .asmx file HelloSoapHeader.asmx. Add a class that is an object representing what is to be placed in the SOAP header by the client, as shown in the following example:

Public Class HelloHeader
   Inherits System.Web.Services.Protocols.SoapHeader

   Public Username As String
   Public Password As String
End Class

The class, representing a SOAP header object, has to inherit from the SoapHeader class from System.Web.Services.Protocols.SoapHeader. The SoapHeader class serializes the payload of the <soap:header> element into XML for you. In this example, the SOAP header requires two elements — a username and a password, both of type String. The names you create in this class are those used for the sub-elements of the SOAP header construction, so it is important to name them descriptively.

The following code shows the Web service class that instantiates an instance of the HelloHeader class:

<WebService(Namespace:="http://www.wrox.com/helloworld")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1, _
  EmitConformanceClaims:=True)> _
Public Class HelloSoapHeader
Inherits System.Web.Services.WebService

    Public myHeader As HelloHeader

    <WebMethod(), SoapHeader("myHeader")> _
    Public Function HelloWorld() As String
        If (myHeader Is Nothing) Then
           Return "Hello World"
        Else
           Return "Hello " & myHeader.Username & ". " & _
              "<br>Your password is: " & myHeader.Password
        End If
    End Function

End Class

The Web service, HelloSoapHeader, has a single WebMethodHelloWorld. Within the Web service class, but outside of the WebMethod itself, create an instance of the SoapHeader class:

Public myHeader As HelloHeader

Now that you have an instance of the HelloHeader class that you created earlier, myHeader, you can use that instantiation in your WebMethod. Because Web services can contain any number of WebMethods, it is not necessary for all WebMethods to use an instantiated SOAP header. You specify whether a WebMethod uses a particular instantiation of a SOAP header class by placing the SoapHeader attribute before the WebMethod declaration:

<WebMethod(), SoapHeader("myHeader")> _
Public Function HelloWorld() As String
   ' Code here
End Function

Here, the SoapHeader attribute takes a string value of the name of the instantiated SoapHeader class — in this case, myHeader.

The WebMethod actually makes use of the myHeader object. If the myHeader object is not found (meaning the client did not send a SOAP header with the constructed SOAP message), then a simple "Hello World" is returned. However, if values are provided in the header of the SOAP request, then those values are used in the returned string value.

Consuming a Web Service Using SOAP Headers

It is not difficult to build an ASP.NET application that makes a SOAP request to a Web service using SOAP headers. As with Web services that do not include SOAP headers, you make a Web reference to the remote Web service directly in Visual Studio.

For the ASP.NET page, create a simple page with a single Label control. The output of the Web service is placed in this control. Following is the code for the ASP.NET page:

<%@ Page Language="VB" %>

<script runat="server">
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim ws As New localhost.HelloSoapHeader()
        Dim wsHeader As New localhost.HelloHeader()

        wsHeader.Username = "Bill Evjen"
        wsHeader.Password = "Bubbles"
        ws.HelloHeaderValue = wsHeader

        Label1.Text = ws.HelloWorld()
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Working with SOAP headers</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" Runat="server"></asp:Label>
    </div>
    </form>
</body>
</html>

Two objects are instantiated. The first is the actual Web service, HelloSoapHeader. The second, which is instantiated as wsHeader, is the SoapHeader object. After both of these objects are instantiated and before making the SOAP request in the application, you construct the SOAP header. This is as easy as assigning values to the Username and Password properties of the wsHeader object. After these properties are assigned, you associate the wsHeader object to the ws object through the use of the HelloHeaderValue property. After you have made the association between the constructed SOAP header object and the actual WebMethod object (ws), you can make a SOAP request, just as you would normally do:

Label1.Text = ws.HelloWorld()

Running the page produces the result shown in Figure 28-19.

Figure 28-19

Figure 28.19. Figure 28-19

Note that the SOAP request reveals that the SOAP header was indeed constructed into the overall SOAP message, as shown in the following SOAP request:

<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Header>
      <HelloHeader xmlns="http://www.wrox.com/helloworld/">
         <Username>Bill Evjen</Username>
         <Password>Bubbles</Password>
      </HelloHeader>
   </soap:Header>
   <soap:Body>
      <HelloWorld xmlns="http://www.wrox.com/helloworld/" />
   </soap:Body>
</soap:Envelope>

This returns the SOAP response shown here:

<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Body>
      <HelloWorldResponse xmlns="http://www.wrox.com/helloworld/">
         <HelloWorldResult>Hello Bill Evjen. Your password is:
          Bubbles</HelloWorldResult>
      </HelloWorldResponse>
   </soap:Body>
</soap:Envelope>

Requesting Web Services Using SOAP 1.2

Most Web services use SOAP version 1.1 for the construction of their messages. However, SOAP 1.2 became a W3C Recommendation in June 2003 (see www.w3.org/TR/soap12-part1/). The nice thing about XML Web Services in the .NET Framework platform is that they are capable of communicating in both the 1.1 and 1.2 versions of SOAP.

In an ASP.NET application that is consuming a Web service, you can control whether the SOAP request is constructed as a SOAP 1.1 message or a 1.2 message. The next example changes the previous example to use SOAP 1.2 instead of the default setting of SOAP 1.1:

<%@ Page Language="VB" %>

<script runat="server">
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim ws As New localhost.HelloSoapHeader()
        Dim wsHeader As New localhost.HelloHeader()

        wsHeader.Username = "Bill Evjen"
        wsHeader.Password = "Bubbles"
        ws.HelloHeaderValue = wsHeader

        ws.SoapVersion = System.Web.Services.Protocols.SoapProtocolVersion.Soap12

        Label1.Text = ws.HelloWorld()
    End Sub
</script>

This example first provides an instantiation of the Web service object and uses the new SoapVersion property. The property takes a value of System.Web.Services.Protocols.SoapProtocolVersion.Soap12 to work with SOAP 1.2 specifically. With this bit of code in place, the SOAP request takes the structure shown here:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Header>
<HelloHeader xmlns="http://www.wrox.com/helloworld/">
         <Username>Bill Evjen</Username>
         <Password>Bubbles</Password>
      </HelloHeader>
   </soap:Header>
   <soap:Body>
      <HelloWorld xmlns="http://www.wrox.com/helloworld/" />
   </soap:Body>
</soap:Envelope>

One difference between the two examples is the xmlns:soap namespace that is used. The difference actually resides in the HTTP header. Comparing the SOAP 1.1 and SOAP 1.2 messages, you can see a difference in the Content-Type attribute. In addition, the SOAP 1.2 HTTP header does not use the soapaction attribute because this is now combined with the Content-Type attribute.

You can turn off either SOAP 1.1 or SOAP 1.2 capabilities with the Web services that you build by making the proper settings in the web.config file, as illustrated here in this snippet of configuration code:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   <system.web>
      <webServices>
         <protocols>
            <remove name="HttpSoap"/> <!-- Removes SOAP 1.1 abilities -->
            <remove name="HttpSoap1.2"/> <!-- Removes SOAP 1.2 abilities -->
         </protocols>
      </webServices>
   </system.web>
</configuration>

Visual Basic and System.Web.Services

The SOAP Toolkit provides a number of wizards to navigate most of the obstacle course required to set up a Web service, but the .NET Framework class library provides the abstract classes. The System.Web.Services namespace provides four classes and three other namespaces that enable programmatic exposure of methods to the Web.

System.Web.Services Namespace

The System.Web.Services namespace includes the following component classes:

  • WebService

  • WebMethodAttribute

  • WebServiceAttribute

  • WebServicesBindingAttribute

The WebService class is the base class from which all the ASP.NET services are derived, and it includes access to the public properties for Application, Context, Server, Session, Site, and User. ASP programmers will recognize these objects from the ASP namespace. Web services can access the IIS object model from the WebService class, including application-level variables:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace:="http://www.lipperweb.com/namespace")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
Public Class Util
    Inherits System.Web.Services.WebService

   <WebMethod(Description:="Application Hit Counter", EnableSession:=False)> _
    Public Function HitCounter() As String

       Dim HitCounter As Integer

       If (Application("HitCounter") Is DBNull.Value) Then
          Application("HitCounter") = 1
       Else
          Application("HitCounter") = Application("HitCounter") + 1
       End If

       HitCounter = Application("HitCounter")

       Return HitCounter

    End Function

End Class

WebService is an optional base class, used only if access to ASP.NET objects is desired. The WebMethodAttribute class, however, is a necessity if the class needs to be available over the Web.

The WebServiceAttribute class is similar to the WebMethodAttribute class in that it enables the addition of the description string to an entire class, rather than method by method. We recommend adding it before the previous class declaration:

<WebService(Description:="Common Server Variables")> _
Public Class ServerVariables
    Inherits System.Web.Services.WebService

Instead of using WSDL in the contract to describe these services, the System.Web.Services namespace provides programmatic access to these properties. IIS Service Discovery uses these descriptions when queried. This way, you have removed the necessity to struggle with myriad protocols surrounding Service Contract Language and SOAP.

System.Web.Services.Description Namespace

The System.Web.Services.Description namespace provides a host of classes that provide total management of the WSDL descriptions for your Web service. This object manages every element in the WSDL schema as a class property.

For example, the preceding discussion on the benefits of WSDL description mentioned being able to query a Web service about its methods and parameters. The System.Web.Services.Description namespace provides methods for the discovery of methods and parameters, gathering the information from the service contract and providing it to the object model in Visual Basic code.

When working on the HTTP GET protocol (as opposed to SOAP, for instance), simply pass in the required sEmail parameter through the use of a querystring. You can find details about this in the Web service's WSDL description. In the successive <wsdl:message> sections, you can find all parameter information for all three protocols, including HTTP GET (if enabled via the web.config file):

<wsdl:message name="IsValidEmailHttpGetIn">
   <wsdl:part name="sEmail" type="s:string" />
</wsdl:message>
<wsdl:message name="IsValidEmailHttpGetOut">
   <wsdl:part name="Body" element="tns:boolean" />
</wsdl:message>

Invoking this Web service using HTTP GET, use the following construct:

http://localserver/[email protected]

Note that HTTP GET is disabled by default because it is deemed a security risk. If you wish to enable HTTP GET for your XML Web Services, then configure it for this in the web.config file of your Web service solution, as shown here:

<configuration>
   <system.web>
      <webServices>
         <protocols>
            <add name="HttpGet"/>
         </protocols>
      </webServices>
   </system.web>
</configuration>

System.Web.Services.Discovery Namespace

The System.Web.Services.Discovery namespace provides access to all of the wonderful features of the .disco files on a dynamic basis. Because Microsoft is currently trying to integrate Web services as a remoting protocol and is not pushing the public service side as much, you don't see the use of .disco files as often in the Microsoft side of things. Your business partner might be using them, though, so this namespace proves useful. For instance, you can access the DiscoveryDocument using the Discovery class:

Imports System.Web.Services.Discovery

ReadOnly Property DiscoveryDocument(strURL As String) As DiscoveryDocument
   Get
      DiscoveryDocument = DiscoveryClientProtocol.Discover(strURL)
   End Get
End Property

Like the System.Web.Services.Description namespace, the System.Web.Services.Discovery namespace provides many tools to build a .disco document on the fly.

System.Web.Services.Protocols Namespace

All of the wire service problems solved with HTTP and SOAP are handled in the System.Web.Services.Protocols namespace. When handling references to classes also referenced in other Web service namespaces, the System.Web.Services.Protocols namespace proves to be a handy tool. Objects referenced by the System.Web.Services.Protocols namespace include the following (among others):

  • Cookies, per RFC 2019

  • HTML forms

  • HTTP request and response

  • MIME

  • Server

  • SOAP, including SoapException, the only error-handling mechanism

  • URIs and URLs

  • XML

The System.Web.Services.Protocols namespace is particularly handy for managing the connection type by a client. A consumer of a Web service can use the HTTP GET or HTTP POST protocol to call a service, as well as the HTTP SOAP protocol. Microsoft's .NET initiative focuses on SOAP as the ultimate means of connecting disparate data sources. The System.Web.Services.Protocols.SoapDocumentMethodAttribute class enables developers to set special attributes of a public method for when a client calls it using SOAP:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace:="http://www.lipperweb.com/namespace")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
Public Class Util
    Inherits System.Web.Services.WebService

  <SoapDocumentMethod(Action:="http://MySoapMethod.org/Sample", _
   RequestNamespace:="http://MyNamespace.org/Request", _
   RequestElementName:="GetUserNameRequest", _
   ResponseNamespace:="http://MyNamespace.org/Response", _
   ResponseElementName:="GetUserNameResponse")> _
   WebMethod(Description:="Obtains the User Name")> _
  Public Function GetUserName()
    '...
  End Function

End Class

Architecting with Web Services

Web services impart two remarkable benefits to users — one rather obvious, the other less so. First, they replace common binary RPC formats, such as DCOM, CORBA, and RMI. Because these use a proprietary communication protocol, they are significantly less architecturally flexible than Web services. As devices utilize more and more of the Internet, platform neutrality will be a great advantage.

Less obvious but more important, Web services will be used to transfer structured business communications in a secure manner, potentially ending the hold that Sterling has on the Electronic Data Interchange (EDI) market. HTTPS with 128-bit SSL can provide the security necessary for intracompany information transfer. Furthermore, Microsoft has recently (as of this writing) released Web Services Enhancements 3.0 (WSE), as well as the Windows Communication Foundation (WCF), which enables you to easily use WS-Security and other advanced protocols to apply credentials, encryption, and digital signing to your SOAP messages in an easy and straightforward manner.

Why Web Services?

Web services are remarkably easy to deploy with Visual Basic. The key to remoting with Web services is the WSDL contract — written in the dense WSDL protocol shown earlier. IIS 5.0, 6.0, and 7.0 does that in conjunction with the .NET Framework, analyzing the VB code and dynamically generating the WSDL code for the contract.

In addition, Web services are inherently cross-platform, even when created with Microsoft products. Yes, you have heard this before, but so far it seems to be true. The standard XML schemas are centrally managed, and IBM mostly built the WSDL specification, so Microsoft seems to have been up to standard on this one.

Finally, they best represent where the Internet is heading — toward an architecturally neutral collection of devices, rather than millions of PCs surfing the World Wide Web. Encapsulating code so that you can simply and easily allow cell phones to use your logic is a major boon to developers, even if they do not know it yet.

How This All Fits Together

Note that Web services are not a feature of the .NET Framework per se. In fact, Web services run fine on Windows NT4 SP6, with the SOAP Toolkit installed. You can do most anything you are doing here with VB6 and IIS 4.0.

However, the .NET Framework encapsulates the Web service protocol into objects. It is now an integrated part of the strategy, rather than an add-on. If you are currently working in a VB6 environment, look at the SOAP Toolkit (downloadable from MSDN at http://msdn.microsoft.com/webservices), and understand that the services you build are available not only to different flavors of Windows, but also to IBM and Sun platforms.

The goal of Web services is to provide a loosely coupled, ubiquitous, universal information exchange format. Toward that end, SOAP is not the only mechanism for communicating with Web services — the HTTP GET and HTTP POST protocols are also supported by the .NET Framework. Response is via HTTP, just like normal RPCs with SOAP. This enables legacy Web applications to make use of Web services without the benefit of the .NET Framework.

State Management for XML Web Services

The Internet is stateless by nature. Many of the techniques used for managing state in ASP.NET Web applications are the same techniques you can use within the XML Web Services built on the .NET platform. Remember that XML Web Services are part of the ASP.NET model, and both application types have the same objects at their disposal.

Therefore, just like an ASP.NET application, XML Web Services can also use the Application object or the Session object. These sessions can also be run in the same process as the XML Web Services application itself — out of process, using the .NET StateServer or by storing all the sessions within SQL Server.

To use sessions within XML Web Services built on the .NET platform, you must turn on this capability within the WebMethod attribute by using the EnableSession property. By default, the EnableSession property is set to False, so to use the HTTPSessionState object, set this property to True, as shown here:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace:="http://www.lipperweb.com/namespace")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
Public Class Service
    Inherits System.Web.Services.WebService

   <WebMethod(EnableSession:=True)> _
   Public Function SessionCounter() As Integer
      If Session("Counter") Is Nothing Then
         Session("Counter") = 1
      Else
         Session("Counter") = CInt(Session("Counter")) + 1
      End If

      Return CInt(Session("Counter"))
   End Function

End Class

The EnableSession property goes directly in the parentheses of the WebMethod declaration. This property takes a Boolean value and needs to be set to True in order to work with the Session object.

Security in Web Services

Opening up a procedure call to remoting makes applications vulnerable to accidents, poor end-user implementation, and crackers. Any application design needs to include some level of security. Web services demand the inclusion of security.

Security problems with Web services fall into two categories: interception and unauthorized use. SOAP messages intercepted by crackers potentially expose private information, such as account numbers and passwords, to the public. At best, unauthorized use costs money, and at worst it wreaks havoc within a system.

Very few of the concepts discussed here are things we would like to see in the hands of those wearing the black hats. Even the simple validation service handles e-mail addresses — a valuable commodity in this world of "opt in" spamming. If you add social security or account numbers to the service, then this becomes even more of a concern. Fortunately, the wire transport of choice — HTTPS — provides a 128-bit solution.

In addition, as mentioned earlier, by using Microsoft's Web Services Enhancements (WSE) and the Windows Communication Foundation capabilities, you now can easily apply security standards such as WS-Security to your SOAP messages.

The Secure Sockets Layer

The Secure Sockets Layer (SSL) is a protocol consumed by HTTP in the transfer of Internet data from the web server to the browser. On the Web, the process works like this:

  1. The user calls a secure Web document, and a unique public key is generated for the client browser, using the server's root certificate.

  2. A message encrypted with the server's public key is sent from the browser.

  3. The server can decrypt the message using its private key.

The protocol in the URI represents how HTTP would appear if it were changed to HTTPS:

<address uri="https://aspx.securedomains.com/evjen/Validate.asmx" />

The service would then make an SSL call to the server. Remember that SSL is significantly slower than HTTP, so you will suffer a performance hit. Given the sensitivity of much of the information passing over Web services, however, it is probably worth the slowdown.

Directory-Level Security

You also have the option to code security into your applications. This solves different problems from SSL, and in fact you may want to combine the two services for a complete security solution.

Unauthorized access is a potential problem for any remote system, but even more so for Web services. The open architecture of the system provides crackers with all the information they need to plan an attack. Fortunately, simplicity is often the best defense. Using the NT security options already on the server is the best way to defend against unauthorized users.

You can use NTFS permissions for individual directories within an application and require users to provide a valid username and password combination if they want to access the service.

Web service security is a large area to cover. For more information, refer to the documentation included with the .NET Framework SDK.

The best approach to security is to use SSL and directory-level security together. It is slow, and at times inconvenient, but this is a small price to pay for the heightened level of security. Though this is different from the traditional role-based COM + security, it is still very effective for running information across the wire.

Other Types of Security

The Windows platform also provides for other forms of security. For instance, the Windows CryptoAPI supplies access to most of the commonly used encryption algorithms — aside from the protocols used in the Secure Sockets Layer. Digital certificates (sort of a personal form of SSL ServerCertificates) are now rapidly becoming a powerful force in security.

The Downside

There is a downside to any distributed architecture. We've covered most of them in this chapter and suggested workarounds — security, state, speed, and connectivity. Let's go over them once more to ensure that Web services are the way to go.

Security

The key to the issue and solution of security problems is the management of client expectations. If Web services are built securely to begin with, then you won't face situations that draw concern or scrutiny. Consider the security of everything you write. It's fairly easy, and the payoff is great.

State

State is less of a problem in a distributed architecture because in Windows DNA, Microsoft has been saying for years that n-tier statefulness has to go. Most developers are used to the idea, but if you are not, then you need to get on the boat with the rest of us. Architect your solutions to be loosely coupled, which is what Web services are designed to do.

Transactions

Web services are not made for transactional systems. If the web server at MyCompany.com were to access a database at UPS, for example, and the connection dropped in the middle, the lock on the database would remain without giving the network system at UPS a chance to solve the problem. Web services are by nature loosely coupled. They are not designed for tight transactional integration.

A common use of Web services, communication between differing systems, prompted a number of technology architects to design several XML transaction protocols, such as 2PC. These packages provide the two systems with an understanding that the network link will remain stable.

Speed and Connectivity

Speed and connectivity are going to be a continuing problem until we have the ubiquitous bandwidth George Gilder talks about in his book Telecosm (Free Press, 2000). Right now, the majority of Internet devices that could really benefit from Web services — cell phones, PDAs, and the like — are stuck at the paltry 14,000 bits per second currently supported by most wireless providers.

For application development, this is a concern because when the router goes down, the application goes down. Right now, intranets continue to function when the ISP drops the ISDN. With Web services running the links to customers and suppliers, that ISDN line becomes the company lifeline. Redundancy of connections and a firm partnership with your service provider are the only solution.

Where We Go from Here

The cell phone is a listening device. It listens for a call to its network address from the cell network. When it receives one, it follows some logic to handle the call. Sound familiar? This works just like the RPC architecture and will be the format for a new host of devices that listen for Web service calls over the G3 wireless network.

The first lines of the W3C XML group's charter state the following:

"Today, the principal use of the World Wide Web is for interactive access to documents and applications. In almost all cases, such access is by human users, typically working through web browsers, audio players, or other interactive front-end systems. The Web can grow significantly in power and scope if it is extended to support communication between applications, from one program to another."

New business communication will be via XML and Web services, rather than EDI and VANs. Micro-payment may actually become a reality. Scores of promises that the Internet has made since its inception can be fulfilled with Web services and XML. It won't stop there, though. The power of listening devices will bring Web services development into user-to-user markets from business-to-business ones.

It sounds far-fetched, but it is hoped that you can see how the power of Web services on .NET could make this possible. SOAP is not just about replacing the RPC architecture already out there. It is a fundamentally different way to think about the network as the platform.

Summary

This chapter looked at the need for an architecturally neutral, ubiquitous, easy to use, and interoperable system to replace DCOM, RMI, and CORBA. It discussed how Web services fill the gaps successfully because HTTP is used as the language-independent protocol, XML is its language (in WSDL) and transport mechanism, and SOAP enables you to package messages for sending over HTTP.

The chapter also described how to create and consume Web services programmatically using Visual Basic, and discussed the abstract classes provided by the .NET Framework class library to set up and work with Web Services. In particular, it looked at the WebService, WebServiceAttribute, WebMethodAttribute, and WebServiceBindingAttribute component classes of the System.Web.Services namespace, in addition to the System.Web.Services.Description, System.Web.Services.Discovery, and System.Web.Services.Protocols namespaces.

Finally, it outlined some of the downsides to using any distributed architecture (Web services included), but it finished with an optimistic note regarding where Web services might take us in the future.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset