Web Services

Web Services

A great place to begin an exploration of Web services is to define precisely what a Web service is. A Web service is an application that:

  • Runs on a Web server

  • Exposes Web methods to interested callers

  • Listens for HTTP requests representing commands to invoke Web methods

  • Executes Web methods and returns the results

Most Web services expect their Web methods to be invoked using HTTP requests containing SOAP messages. SOAP is an XML-based vocabulary for performing remote procedure calls using HTTP and other protocols. You can read all about it at http://www.w3.org/TR/SOAP. Suppose you write a Web service that publishes Web methods named Add and Subtract that callers can use to add and subtract simple integers. If the service s URL is www.wintellect.com/calc.asmx, here s how a client would invoke the Add method by transmitting a SOAP envelope in an HTTP request. This example adds 2 and 2:

POST /calc.asmx HTTP/1.1 Host: www.wintellect.com Content-Type: text/xml; charset=utf-8 Content-Length: 338 SOAPAction: "http://tempuri.org/Add" <?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> <Add xmlns="http://tempuri.org/"> <a>2</a> <b>2</b> </Add> </soap:Body> </soap:Envelope>

And here s how the Web service would respond:

HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: 353 <?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> <AddResponse xmlns="http://tempuri.org/"> <AddResult>4</AddResult> </AddResponse> </soap:Body> </soap:Envelope>

The Web service s job is to parse the SOAP envelope containing the inputs, add 2 and 2, formulate a SOAP envelope containing the sum of 2 and 2, and return it to the client in the body of the HTTP response. This, at the most elemental level, is what Web services are all about.

Web services written with the .NET Framework also allow their Web methods to be invoked using ordinary HTTP GET and POST commands. The following GET command adds 2 and 2 by invoking the Web service s Add method:

GET /calc.asmx/Add?a=2&b=2 HTTP/1.1 Host: www.wintellect.com

The Web service responds as follows:

HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: 80 <?xml version="1.0" encoding="utf-8"?> <int xmlns="http://tempuri.org/">4</int>

Here s a POST command that adds 2 and 2:

POST /calc.asmx/Add HTTP/1.1 Host: www.wintellect.com Content-Type: application/x-www-form-urlencoded Content-Length: 7 a=2&b=2

And here s the Web service s response:

HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: 80 <?xml version="1.0" encoding="utf-8"?> <int xmlns="http://tempuri.org/">4</int>

As you can imagine, the hard part of writing a Web service is parsing HTTP requests and generating HTTP responses. But as you ll see in the next section and throughout the remainder of this chapter, the .NET Framework insulates developers from the low-level details of HTTP, SOAP, and XML and provides a high-level framework for writing Web services and Web service clients alike.

There are many ways to write Web services. You can write Web services by hand. You can use SOAP toolkits from Microsoft, IBM, and other companies. And you can use the .NET Framework. Because this book is about Microsoft .NET, this chapter is about the latter. Writing Web services with the .NET Framework offers two advantages over all the other methods:

  • The .NET Framework makes writing Web services extremely easy.

  • Web services written with the .NET Framework are managed applications, meaning you shouldn t have to worry about memory leaks, stray pointers, and other maladies that bedevil programmers and cost more than their fair share of development time.

What does it take to write a Web service using the .NET Framework? I m glad you asked, because that s what the next section is about.

Your First Web Service

The ASMX file shown in Figure 11-1 is a complete Web service. It implements two Web methods: Add and Subtract. Both take two integers as input and return an integer as well. Deploying the Web service is as simple as copying it to a directory on your Web server that is URL-addressable. If you put Calc.asmx in wwwroot, the Web service s local URL is http://localhost/calc.asmx.

Calc.asmx demonstrates several important principles of Web service programming using the .NET Framework:

  • Web services are implemented in ASMX files. ASMX is a special file name extension registered to ASP.NET (specifically, to an ASP.NET HTTP handler) in Machine.config.

  • ASMX files begin with @ WebService directives. At a minimum, the directive must contain a Class attribute identifying the class that makes up the Web service.

  • Web service classes can be attributed with optional WebService attributes. The one in this example assigns the Web service a name and a description that show up in the HTML page generated when a user calls up Calc.asmx in his or her browser. The WebService attribute also supports a Namespace parameter that can be used to change the name of the XML namespace that scopes the Web service s members.

  • Web methods are declared by tagging public methods in the Web service class with WebMethod attributes. You can build helper methods into a Web service methods that are used internally by Web methods but that are not exposed as Web methods themselves by omitting the attribute. The WebMethod attributes in Figure 11-1 also assign descriptive text to their Web methods. You ll learn more about Description and other WebMethod parameters in the section entitled The WebMethod Attribute.

  • HTTP, XML, and SOAP are hidden under the hood. You don t have to deal with raw XML data or SOAP messages because the .NET Framework deals with them for you.

    Calc.asmx

    <%@ WebService Language="C#"  %> using System; using System.Web.Services; [WebService (Name="Calculator Web Service", Description="Performs simple math over the Web")] class CalcService { [WebMethod (Description="Computes the sum of two integers")] public int Add (int a, int b) { return a + b; } [WebMethod (Description="Computes the difference between two integers")] public int Subtract (int a, int b) { return a - b; } }
    Figure 11-1

    Calc Web service.

Despite its brevity, Calc.asmx is a full-blown Web service when installed on a Web server outfitted with ASP.NET. Its Web methods can be invoked with SOAP, HTTP GET, and HTTP POST, and it s capable of returning output in SOAP responses or simple XML wrappers. All we need now is a way to test it out. The .NET Framework lends a hand there too.

Testing a Web Service

How do you test an ASMX Web service? Simple: just call it up in your browser. To demonstrate, copy Calc.asmx to wwwroot and type

http://localhost/calc.asmx

in your browser s address bar. You ll be greeted with the screen shown in Figure 11-2. What happened? ASP.NET responded to the HTTP request for Calc.asmx by generating an HTML page that describes the Web service. The name and description in the ASMX file s WebService attribute appear at the top of the page. Underneath is a list of Web methods that the service exposes, complete with the descriptions spelled out in the WebMethod attributes.

Figure 11-2

Calc.asmx as seen in Internet Explorer.

Like what you ve seen so far? It gets better. Click Add near the top of the page, and ASP.NET displays a page that you can use to test the Add method (Figure 11-3). ASP.NET knows the method name and signature because it reads them from the metadata in the DLL it compiled from Calc.asmx. It even generates an HTML form that you can use to call the Add method with your choice of inputs. Type 2 and 2 into the a and b boxes and click Invoke. The XML returned by the Web method appears in a separate browser window (Figure 11-4).

Figure 11-3

Test page for the Add method.

Figure 11-4

XML returned by the Add method.

The forms that ASP.NET generates on the fly from ASMX files enable you to test the Web services that you write without writing special clients to test them with. They also let you explore a Web service built with the .NET Framework simply by pointing your browser to it. For kicks, type the following URL into your browser s address bar:

http://terraservice.net/terraservice.asmx

That s the URL of the Microsoft TerraService, an ultra-cool Web service that provides a programmatic interface to a massive database of geographic data known as the Microsoft TerraServer. Don t worry about the details just yet; you ll be using TerraService to build a Web service client later in this chapter. But do notice how much you can learn about TerraService simply by viewing the page that ASP.NET generated for it.

Web Services and Code-Behind

You can use code-behind to move Web service classes out of ASMX files and into separately compiled DLLs. Figure 11-5 shows how Calc.asmx looks after it s modified to take advantage of code-behind. The ASMX file now contains just one statement. The class referenced in that statement is implemented in Calc.cs. The following command compiles Calc.cs into a DLL named Calc.dll:

csc /t:library calc.cs

Once compiled, the DLL must be placed in the application root s bin subdirectory (for example, wwwroot\bin).

Calc.asmx

<%@ WebService  %>

Calc.cs

using System; using System.Web.Services; [WebService (Name="Calculator Web Service", Description="Performs simple math over the Web")] class CalcService { [WebMethod (Description="Computes the sum of two integers")] public int Add (int a, int b) { return a + b; } [WebMethod (Description="Computes the difference between two integers")] public int Subtract (int a, int b) { return a - b; } }
Figure 11-5

Calc Web service with code-behind.

Code-behind offers the same benefits to Web services that it offers to Web pages: it catches compilation errors before the service is deployed, and it enables you to write Web services in languages that ASP.NET doesn t natively support. For an example of ASP.NET code written in managed C++ and for a refresher on code-behind in general, turn back to Chapter 5.

The WebService Base Class

Very often when you see ASMX code samples, the Web service classes inside them derive from a class named WebService, as in

class CalcService : WebService { ... }

WebService belongs to the System.Web.Services namespace. It contributes properties named Application, Session, Context, Server, and User to derived classes, enabling a Web service to access the ASP.NET objects with the same names. If you don t use these objects in your Web service for example, if you don t use application state or session state you don t need to derive from WebService either.

The WebMethod Attribute

The WebMethod attribute tags a method as a Web method. The .NET Framework automatically exposes such methods as Web methods when they re implemented inside a Web service. WebMethod is capable of doing much more, however, than simply letting the framework know which methods are Web methods and which are not; it also supports the following parameters:

Parameter Name

Description

BufferResponse

Enables and disables response buffering

CacheDuration

Caches responses generated by this method for the specified number of seconds

Description

Adds a textual description to a Web method

EnableSession

Enables and disables session state for this Web method

MessageName

Specifies the Web method s name

TransactionOption

Specifies the transactional behavior of a Web method

CacheDuration is the ASMX equivalent of an @ OutputCache directive in an ASPX or ASCX file: it caches a method s output so that subsequent requests will execute more quickly. Suppose, for example, that you write a Web method that returns the current time:

[WebMethod] public string GetCurrentTime () { return DateTime.Now.ToShortTimeString (); }

Since ToShortTimeString returns a string that includes minutes but not seconds, it s wasteful to execute it too often. The following method declaration uses CacheDuration to cache the output for 10 seconds at a time:

[WebMethod (CacheDuration="10")] public string GetCurrentTime () { return DateTime.Now.ToShortTimeString (); }

Now the data that the method returns could be stale by a few seconds, but if the Web service is getting pounded with calls to GetCurrentTime, the load on it will be reduced commensurately.

Web services enjoy access to the same session state facilities that conventional ASP.NET applications do. By default, however, session state is disabled for Web methods. You can enable it with WebMethod s EnableSession parameter. If you want to use session state in a Web service, derive from WebService (to inherit its Session property) and tag each Web method that uses session state with EnableSession= true :

class CalcService : WebService { [WebMethod (EnableSession="true", Description="Adds an item to a shopping cart")] public void AddToCart (Item item) { ShoppingCart cart = (ShoppingCart) Session["MyShoppingCart"]; cart.Add (item); } }

Session state utilization is less common in Web services than in conventional Web applications, but it is an option nonetheless.

The MessageName parameter lets you assign a Web method a name other than that of the method that implements it. For example, suppose that you build two Add methods into a Web service one that adds integers and another that adds floating point values and you tag both of them as Web methods:

[WebMethod] public int Add (int a, int b) { return a + b; } [WebMethod] public float Add (float a, float b) { return a + b; }

The only problem with this code is that it doesn t compile. C# methods can be overloaded, but Web methods cannot. The solution? Either change the method names or add MessageName parameters to the WebMethod attributes, as demonstrated here:

[WebMethod (MessageName="AddInts")] public int Add (int a, int b) { return a + b; } [WebMethod (MessageName="AddFloats")] public float Add (float a, float b) { return a + b; }

Now the C# methods remain overloaded, but the corresponding Web methods are named AddInts and AddFloats.

The Web Services Description Language

If other developers are to consume (that is, write clients for) a Web service that you author, they need to know what Web methods your service publishes, what protocols it supports, the signatures of its methods, and the Web service s location (URL), among other things. All this information and more can be expressed in a language called the Web Services Description Language, or WSDL for short.

WSDL is a relatively new standard. It s an XML vocabulary devised by IBM, Microsoft, and others. Its syntax is documented at http://www.w3.org/TR/wsdl. I won t describe the details of the language here for several reasons. First, the details are already documented in the spec. Second, WSDL is a language for machines, not humans. Third, it s trivial to generate a WSDL contract for a Web service built with the .NET Framework: simply point your browser to the Web service s URL and append a WSDL query string, as in

http://www.wintellect.com/calc.asmx?wsdl

Figure 11-6 shows the result. Scan through it and you ll find a service element that describes the Web service; operation elements that document the operations, or Web methods, that the service supports; binding elements that document the protocols that the Web methods support; and other descriptive information.

Figure 11-6

WSDL contract for Calc.asmx.

When you publish a Web service, you should also publish a WSDL contract describing it. For a Web service built with the .NET Framework, the contract is usually nothing more than a URL with ?wsdl on the end. Other developers can use the contract to write clients for your Web service. Typically, they don t read the contract themselves. Instead, they run it through a tool that generates a wrapper class containing all the elements needed to talk to a Web service. The .NET Framework SDK includes one such tool: it s called Wsdl.exe. You ll learn all about it later in this chapter when we turn our attention from Web services to Web service clients.

Web Services and Complex Data Types

It s not hard to understand how simple data types can be passed to and from Web methods. After all, integers and other primitive types are defined in one form or another on virtually every platform. But what about more complex types? What if, for example, you define a custom class or struct and want to use it as an input parameter or return value for a Web method? Are complex types supported, and if so, how do you declare them so that they become an intrinsic part of the Web service?

Complex types are supported, and they work very well because virtually any type can be represented in XML. As an example, consider the Web service in Figure 11-7. It exposes a Web method named FindStores that accepts a state abbreviation (for example, CA ) as input. FindStores calls a local method named FindByState, which queries the Pubs database that comes with Microsoft SQL Server for all the bookstores in the specified state and returns the results in an array of Bookstore objects. (Observe that FindByState is not a Web method because it lacks a WebMethod attribute.) FindStores returns the array to the client. Bookstore is a custom type defined in the ASMX file.

Figure 11-8 shows the XML returned when FindStores is called with the input string CA . The array of Bookstore objects has been serialized into XML. The serialization is performed by the .NET Framework s System.Xml.Serialization.XmlSerializer class, otherwise known as the XML serializer. A client application that receives the XML and that has a schema describing the structure and content of the data can rehydrate the information into Bookstore objects. Or it can take the raw XML and do with it as it pleases.

Locator.asmx

<%@ WebService Language="C#"  %> using System; using System.Web.Services; using System.Data; using System.Data.SqlClient; [WebService (Name="Bookstore Locator Service", Description="Retrieves bookstore information from the Pubs database")] class LocatorService { [WebMethod (Description="Finds bookstores in a specified state")] public Bookstore[] FindStores (string state) { return FindByState (state); } Bookstore[] FindByState (string state) { SqlDataAdapter adapter = new SqlDataAdapter ("select * from stores where state = \'" + state + "\'",  "server=localhost;database=pubs;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); DataTable table = ds.Tables[0]; Bookstore[] stores = new Bookstore[table.Rows.Count]; for (int i=0; i<table.Rows.Count; i++) { stores[i] = new Bookstore ( table.Rows[i]["stor_name"].ToString ().TrimEnd (new char[] { ' ' }), table.Rows[i]["stor_address"].ToString ().TrimEnd (new char[] { ' ' }),

 table.Rows[i]["city"].ToString ().TrimEnd (new char[] { ' ' }), table.Rows[i]["state"].ToString ().TrimEnd (new char[] { ' ' }) ); } return stores; } } public class Bookstore { public string Name; public string Address; public string City; public string State; public Bookstore () {} public Bookstore (string name, string address, string city, string state) { Name = name; Address = address; City = city; State = state; } }

Figure 11-7

Bookstore locator Web service.

Figure 11-8

XML returned by the FindStores method.

Where might a client obtain an XML schema describing the Bookstore data type? From the service s WSDL contract, of course. Sneak a peek at Locator.asmx s WSDL contract and you ll see the Bookstore data type (and arrays of Bookstores) defined this way in the contract s types element:

<s:complexType name="ArrayOfBookstore"> <s:sequence> <s:element minOccurs="0" maxOccurs="unbounded" name="Bookstore" nillable="true" type="s0:Bookstore" /> </s:sequence> </s:complexType> <s:complexType name="Bookstore"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="Name" nillable="true" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="Address" nillable="true" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="City" nillable="true" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="State" nillable="true" type="s:string" /> </s:sequence> </s:complexType>

Given these definitions, a client can define a Bookstore class of its own and initialize arrays of Bookstore objects by deserializing Bookstore elements. It s not as hard as it sounds. If the client is written with the .NET Framework, tools generate the class definitions for you and the framework handles the deserialization.

As Locator.asmx demonstrates, it s not difficult to write Web methods that use custom types. There are, however, two gotchas to be aware of:

  • Because query strings are limited to passing simple name/value pairs, you can t pass complex types to a Web method using HTTP GET and POST. That s not a limitation if you use SOAP to invoke Web methods, but it does prevent ASP.NET from generating test pages for methods that accept complex types. If you go to a test page and see the warning No test form is available because this method does not support HTTP GET or something to that effect, you ve found a method that accepts an input parameter that can t be represented in a query string. ASP.NET test forms invoke methods using HTTP GET commands.

  • Any fields or properties declared in a class or struct that s passed to or from a Web method must be public if they re to be serialized when instances of the class or struct are serialized. That s because the .NET Framework s XML serializer will not serialize nonpublic members.

Keep these caveats in mind and you ll have few problems combining Web methods and custom data types.

Web Service Discovery DISCO

Once a client has a WSDL contract describing a Web service, it has all the information it needs to make calls to that Web service. But when you publish a Web service by making it available on a Web server, how do clients find out where to get a WSDL contract? For that matter, how do clients know that your Web service exists in the first place?

The answer comes in two parts: DISCO and Universal Description, Discovery, and Integration, better known as UDDI. The former is a file-based mechanism for local Web service discovery that is, for getting a list of available Web services from DISCO files deployed on Web servers. The latter is a global Web service directory that is itself implemented as a Web service. UDDI is discussed in the next section.

The DISCO (short for discovery ) protocol is a simple one that revolves around XML-based DISCO files. The basic idea is that you publish a DISCO file on your Web server that describes the Web services available on it and perhaps on other servers as well. Clients can interrogate the DISCO file to find out what Web services are available and where the services WSDL contracts can be found. As an example, suppose you publish two Web services and their URLs are as follows:

  • http://www.wintellect.com/calc.asmx

  • http://www.wintellect.com/locator.asmx

To advertise these Web services, you can deploy the following DISCO file at a well-known URL on your server. The contractRef elements identify the URLs of the Web services WSDL contracts. URLs can be absolute or relative (relative to the directory in which the DISCO file resides). The optional docRef attributes identify the locations of documents describing the Web services, which, because of the self-documenting nature of Web services built with the .NET Framework, are typically the ASMX files themselves:

<?xml version="1.0" ?> <discovery xmlns="http://schemas.xmlsoap.org/disco/" xmlns:scl="http://schemas.xmlsoap.org/disco/scl/"> <scl:contractRef ref="http://www.wintellect.com/calc.asmx?wsdl" docRef="http://www.wintellect.com/Calc.asmx" /> <scl:contractRef ref="http://www.wintellect.com/locator.asmx?wsdl" docRef="http://www.wintellect.com/Locator.asmx" /> </discovery>

If you d prefer, you can write DISCO files for individual Web services and reference them in a master DISCO file using discoveryRef elements. Here s a DISCO file that points to other DISCO files. Once more, URLs can be absolute or relative:

<?xml version="1.0" ?> <discovery xmlns="http://schemas.xmlsoap.org/disco/"> <discoveryRef ref="http://www.wintellect.com/calc.disco" /> <discoveryRef ref="http://www.wintellect.com/locator.disco" /> </discovery>

A third option is to deploy a VSDISCO file to enable dynamic discovery. The following VSDISCO file automatically exposes all ASMX and DISCO files in a host directory and its subdirectories, with the exception of those subdirectories noted with exclude elements:

<?xml version="1.0" ?> <dynamicDiscovery xmlns="urn:schemas-dynamicdiscovery:disco.2000-03-17"> <exclude path="_vti_cnf" /> <exclude path="_vti_pvt" /> <exclude path="_vti_log" /> <exclude path="_vti_script" /> <exclude path="_vti_txt" /> </dynamicDiscovery>

How does dynamic discovery work? ASP.NET maps the file name extension .vsdisco to an HTTP handler that scans the host directory and subdirectories for ASMX and DISCO files and returns a dynamically generated DISCO document. A client that requests a VSDISCO file gets back what appears to be a static DISCO document.

For security reasons, Microsoft disabled dynamic discovery just before version 1.0 of the .NET Framework shipped. You can reenable it by uncommenting the line in the httpHandlers section of Machine.config that maps *.vsdisco to System.Web.Services.Discovery.DiscoveryRequestHandler and granting the ASPNET account permission to access the IIS metabase. Microsoft highly discourages dynamic discovery for fear of compromising your Web server, and a bug in version 1.0 of the .NET Framework SDK prevents most DISCO-aware tools from working with VSDISCO anyway. My advice is to forget that VSDISCO files even exist and use static DISCO files instead.

To further simplify Web service discovery, you can link to a master DISCO file from your site s default HTML document. For example, suppose the default HTML document at www.wintellect.com is Default.html and that the same directory also holds a discovery document named Default.disco. Including the following HTML in Default.html enables most tools that read DISCO files to accept the URL www.wintellect.com (as opposed to www.wintellect.com/default.disco):

<html> <head> <link type="text/html" rel="alternate" href="Default.disco"> </head> </html>

Visual Studio .NET (specifically, its Add Web Reference command) reads DISCO files; so does the Disco.exe utility that comes with the .NET Framework SDK.

DISCO s chief disadvantage is that you can t read a DISCO file if you don t have its URL. So how do you find a Web service if you don t even have a URL to start with? Can you spell U-D-D-I?

Web Service Discovery UDDI

UDDI is an abbreviation for Universal Description, Discovery, and Integration. Jointly developed by IBM, Microsoft, and Ariba and supported by hundreds of other companies, UDDI is a specification for building distributed databases that enable interested parties to discover each other s Web services. No one company owns the databases; anyone is free to publish a UDDI-based business registry. Operator sites have already been established by IBM and Microsoft and are likely to be the first of many such sites that will come on line in the future.

UDDI sites are themselves Web services. They publish a pair of SOAP-based APIs: an inquiry API for inquiring about companies and their Web services and a publisher API for advertising a company s Web services. Anyone can call the inquiry API, but operator sites typically limit the publisher API to registered members. Documentation regarding these APIs and other information about UDDI can be found at http://www.uddi.org. At the time of this writing, Microsoft was beta testing a UDDI .NET SDK featuring managed wrapper classes that simplify interactions with UDDI business registries. For the latest information on this and other UDDI development tools proffered by Microsoft, visit http://uddi.microsoft.com/default.aspx.

Most developers will never deal with UDDI APIs directly. Instead, they ll use high-level tools such as Visual Studio .NET to query UDDI business registries and generate wrapper classes that allow them to place calls to the Web services that they find there. The actual placing of UDDI calls will be limited primarily to tools vendors and to clients that wish to locate and bind to Web services dynamically.



Programming Microsoft  .NET
Applied MicrosoftNET Framework Programming in Microsoft Visual BasicNET
ISBN: B000MUD834
EAN: N/A
Year: 2002
Pages: 101

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