Converting Your Code to a Web Service
Converting a business class to a Web service is ridiculously easy. The class can inherit from System.Web.Services.WebService in order to use the ASP.NET objects you've come to know and love (Application, Context, etc.), though this inheritance is not required. Beyond that, you simply need to add the WebMethod attribute to methods that you want to call remotely. Listing 10.1 shows a classic "Hello world" Web service.
Visual Studio 2005 will automatically set up a new class for a Web service to inherit from WebService. Therefore, if you don't want it to do this, you'll need to manually remove it from the class declaration. |
Listing 10.1. "Hello world" Web service (.asmx file)
C#
VB.NET
<%@ WebService Language="C#" Class="Hello" %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
public class Hello
{
[WebMethod]
public string World()
{
return "Hello world!";
}
}
Web service code can live inline in an .asmx file, in a code-behind file (non-compiled), or in an assembly in the /bin folder. The variations of the WebService directive in the .asmx file are:Inline:
<%@ WebService Language="VB" Class="Hello" %>
Imports System
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Public Class Hello
<WebMethod()> Public Function World() As String
Return "Hello world!"
End Function
End Class
Language and a class name must be specified, and code immediately follows.
Code-behind:
<%@ WebService Language="C#" Class="Hello" %>
Additionally, the name of the file containing the code must be specified.
Compiled:
<%@ WebService Language="C#" CodeBehind="mycode.cs" Class="Hello" %>
The class specified must be the name of a compiled class in an assembly in the /bin folder.
<%@ WebService Language="C#" Class="Hello" %>
ASP.NET has a built-in, though somewhat simple, means of testing Web services from a browser. If we pull up the Web service in a browser, we get the page shown in Figure 10.1. At the moment, the page shows some suggestions on how to change the namespace of the Web service (more on that in a minute) and has a link to the one Web method we've specified called "World."
Figure 10.1. The diagnostic Web service page.
[View full size image]

Listing 10.2. The XML generated by the World method of the Hello Web service
Remarkably unimpressive? Perhaps, but it didn't take much code to generate it either. The diagnostic page will give you form fields to send data to the Web service if the methods require them. These are of limited use, though, if you use more complex types, such as classes that contain a number of different properties.There are a couple useful attributes we can use with the class and methods of our Web service. You've already seen WebMethod, and you can add some additional information to it to make it more useful. Listing 10.3 shows all these additional parameters for the WebMethod attribute in use, separated by commas. They're all fairly straightforward in their usage:BufferResponse:
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://tempuri.org/">Hello world!</string>
A Boolean value that, if true, forces the Web service to generate its entire response before sending it back to the calling client. The default is true.CacheDuration:
Number of seconds to cache the response.Description:
A string indicating what the method does. This is used in the diagnostic page as well as in the generated WSDL (more on that in the next section).EnableSession:
A Boolean value that turns on session state when true. This works by issuing an HTTP cookie to track session state. This is not generally useful because it assumes that the calling system will store and return that cookie.MessageName:
A string that gives a method an alias; necessary when you have two methods with the same name but different method signatures.transactionOption:
transactions in Web services are beyond the scope of this discussion.
Listing 10.3. Specifying WebMethod attribute with properties
C#
VB.NET
[WebMethod(BufferResponse=true, CacheDuration=20, Description="stuff",
EnableSession=false, MessageName="test", TransactionOption = TransactionOption.Disabled)]
There is also an attribute called WebService with three available properties, set just before the class declaration, as shown in Listing 10.4. Its properties are even simpler:Description:
<WebMethod(BufferResponse:=True, CacheDuration:=20, _
Description:="stuff", EnableSession:=False, MessageName:="test", _
TransactionOption:=TransactionOption.Disabled)>
A string explaining the overall purpose of the Web service.Name:
A string naming the service.Namespace:
A string that will appear in the XML of the service indicating its namespace. If you're unfamiliar with XML namespaces, they are simply unique identifiers, usually written as URIs. A namespace doesn't necessarily reflect an actual Web address, but because Web addresses are inherently unique, they are frequently used. Web service authors will frequently use actual URIs that point to documentation for the service, but that's entirely optional. It's acceptable to use something like http://dotnetrules/myservice if that's what you desire.
Listing 10.4. Specifying the WebService attribute with properties
C#
VB.NET
[WebService(Description="A fun Web service.",Name="Hello",Namespace="http://happyfun/hello/")]
To some extent, you need to cast aside the design principles mentioned early in the book when you're building a Web service. As we already discussed, trying to implement a true object-oriented API will cause a great deal of data exchange and result in huge messages being sent back and forth between your Web service and the calling system.That doesn't mean that you can't build a service with more complex types. The key to keeping the service limited to one trick is in the way you package the exchange of data. This is dictated by the method signatures you'll use.For example, Amazon.com uses Web services to allow enterprising developers and site publishers to access the Amazon system for product data and even shopping cart manipulation. (The developers do this, of course, because they get a cut of anything bought through their sites via Amazon's affiliate program.) Amazon has a single service called AmazonSearchService, with a number of methods that take various types as parameters and return other types. By using this scheme, a chunk of data is sent in one message to the service and a chunk is sent back, all in one round trip. This is quite different from our earlier design patterns, where we would create various objects and interact with different methods to manipulate them. Doing so in this situation would cause multiple trips to the service to get the desired result, which could cause the system to get bogged down if it got busy. In fact, you could actually put the WebMethod attribute on the get and set portions of a property, but you'd pay dearly in terms of performance.To demonstrate the design of a Web service that serves as an endpoint for a single exchange, we'll create a service that boosts your self-esteem. In the following section, we'll see how to consume the service.Our new Web service should take an object called Person, which contains the person's age and name, and should return an object called Status, which contains a self-esteem boosting message and the amount of money the person is entitled to, based on their age (an old person's dream, for sure).Open a new Visual Studio Web project and add a Web service to the project. Call it EsteemBooster.asmx. To create this service, we'll create two container classes, one called Person and one called Status. The third class, called EsteemBooster, will be the one that actually contains our WebMethod, which is called Boost. The code is shown in Listing 10.5.
<WebService(Description:="A fun Web service.", Name:="Hello", _
Namespace:="http://happyfun/hello/")>
Listing 10.5. The EsteemBooster class (EsteemBooster.asmx)
C#
VB.NET[View full width]
<%@ WebService Language="C#" Class="EsteemBooster" %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Description="Never feel down again.", Namespace="http://esteemshack/booster")]
public class EsteemBooster
{
[WebMethod(Description="Boost the user's self-esteem.")]
public Status Boost(Person peep)
{
Status s = new Status();
s.Message = peep.Name + " is an outstanding person!";
s.Dollars = peep.Age * 10000;
return s;
}
}
public class Person
{
public string Name;
public int Age;
}
public class Status
{
public string Message;
public int Dollars;
}
The structure of these classes should seem familiar enough to you, with the only significant difference being the attributes added before the class and method declarations. Notice that the actual Web service class has only methods. It does not have any properties. This supports our idea that the service should be an efficient endpoint, where one round trip of data does the job.
<%@ WebService Language="VB" Class="EsteemBooster" %>
Imports System
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
<WebService(Description:="Never feel down again.", Namespace:="http://esteemshack/booster")> _
Public Class EsteemBooster
<WebMethod(Description:="Boost the user's self-esteem.")> _
Public Function Boost(peep As Person) As Status
Dim s As New Status()
s.Message = peep.Name + " is an outstanding person!"
s.Dollars = peep.Age * 10000
Return s
End Function
End Class
Public Class Person
Public Name As String
Public Age As Integer
End Class
Public Class Status
Public Message As String
Public Dollars As Integer
End Class