|only for RuBoard|
Zip code verification is a simple but useful example of a service that can be provided over the Web. The sole purpose of this web service is to confirm that a zip code for a given city is valid. Imagine it as just one of several services offered to online businesses to authenticate shipping and billing addresses and minimize fraud. It also solves a real-world problem: someone in California makes an online purchase from a web site in California, provides a California address, but uses a zip code from Texas. Chances are that the order will arrive , albeit late, to the intended address. However, by specifying an improper zip code, the purchaser has circumvented paying sales tax.
Any class that wishes to become a web service must have at least one method that is decorated with a WebMethod attribute. This is the bare minimum requirement for running a web service.
There is also a WebService attribute that is used at the class level, as in:
<WebService(Namespace:="http://www.mydomainhere.com/")> _ Public Class ZipService
Although the WebService attribute is not a runtime requirement, it should be used nonetheless to provide a namespace that uniquely identifies the web service on the Internet. Usually, a company uses its domain to form the namespace. However, the namespace itself does not have to conform to a site that can be reached over the Internet. If a namespace is not given, http://tempuri.org/ is used by default. (This stands for "temp URI" and has nothing to do with Japanese food.) Additionally, the attribute provides properties that allow the name of the service to be specified as well as the service to be described.
Example 10-1 contains the list for the zip code validation web service. The class contains a single method, IsValid , which simply returns True . The actual method implementation is delayed until the end of the chapter, for those who are interested. The goal here is to get the web service to work. Compile it to an assembly named ZipService , since it will be used throughout the rest of the chapter.
'vbc /t:library /r:System.dll '/r:System.Web.Services.dll ZipService.vb Imports System Imports System.Web.Services <WebService(Namespace:="http://www.mydomainhere.com/")> _ Public Class ZipService <WebMethod( )> _ Public Function IsValid(ByVal City As String, _ ByVal Zip As String) As Boolean Return True End Function End Class
Several interesting properties can be used with the WebMethod attribute:
This Boolean indicates whether the response should be buffered before it is sent to the client. When True , the response is buffered first and then sent to the client in its entirety. This is beneficial when a large amount of data is passed. If False , data is sent to the client as it is serialized. This is ideal when small amounts of data are sent. If the BufferedResponse property is not provided, its value defaults to True ; the response to the client is buffered.
This property specifies the number of seconds a response should be held in the cache. A value of (the default) indicates that the response is not cached. Setting this property would not be appropriate for a web service like ZipService because of the variance in the requests (hundreds of different zip codes and cities, with very few identical requests ).
This property associates descriptive text with the method.
If True , this property indicates that session state is maintained for the web method. Its default value is False ; session state is disabled. Maintaining session state causes poor performance and requires that the web service inherit from the WebService class.
This property allows a different name to be associated with a method. It is particularly useful when a web service has several overloaded methods .
To deploy the web service, create a virtual directory called ZipService (see Section 9.6.1 in Chapter 9) and place its DLL in the /bin subdirectory, as follows :
Inetpub wwwroot ZipService <-- ZipService.asmx bin <-- Web service assembly
Once the assembly is in place, a way is needed to find it. To access the web service from a browser, place an .asmx file ( ZipService.asmx ) describing the service in the ZipService virtual directory. The file contains this single line:
<%@ WebService Class="ZipService" %>
As long as the class specified in the WebService entry is in the /bin directory, it will be found.
Web Services from ASP.NET
R ather than compiling the web service to a separate assembly, the entire web service can be defined within an . asmx file such as the following, named ZipService.asmx :
<%@ WebService Language="VB" Class="ZipService" %> Imports System Imports System.Web.Services <WebService( _ Namespace:="http://www.mydomainhere.com/")> _ Public Class ZipService <WebMethod( )> _ Public Function IsValid(ByVal City As String, _ ByVal Zip As String) As Boolean Return True End Function End Class
In this case, a Language directive is required to specify the language used. This directive allows ASP.NET to compile the web service on the fly. The assembly that is generated will be in the ASP.NET cache rather than the /bin directory. Its path is <%windir%>/Microsoft.NET/Framework/<%version%>/Temporary ASP.NET Files/<%assembly%>.
As for testing, ASP.NET does most of the work by generating the necessary pages to test the web service. Navigating to the URL of ZipService.asmx brings up the page shown in Figure 10-1.
Clicking on the Service Description link brings up a description of the web service in Web Service Description Language (WSDL), as Figure 10-2 illustrates. WSDL is a specification that describes a web service by using XML. Notice that the URL shown in Figure 10-2 has ?WSDL appended to it. This is similar to the way a URL is specified for SoapSuds (see Chapter 9). The resulting output is different, though. Remoting uses RPC-encoded SOAP, while ASP.NET web services use what is known as the Document/literal format.
Essentially, SoapSuds expects RPC-encoded SOAP, so ZipService is not available within the remoting framework (yet). However, the WSDL that is produced serves the same purpose. It is used to create a proxy class that allows clients to access the service.
Clicking on the IsValid link shown in Figure 10-1 brings up a test harness for the web method. A test form, shown in Figure 10-3, is generated automatically. It allows individual parameters of the method to be specified and provides a button to invoke the method. At this point, regardless of the parameters passed, the following XML containing the result of the call is returned in the browser:
<?xml version="1.0" encoding="utf-8" ?> <boolean xmlns="http://192.168.1.100/">true</boolean>
Creating a client for a web service is similar to creating one for remoting. The difference is the tool that is used. Attempting to use SoapSuds returns the following error (because it expects a different encoding than that provided by ASP.NET):
Error: Invalid schema data., No Bindings with Soap, Rpc and Encoded elements.
Another available utility, wsdl.exe , can generate the proxy instead. The program provides all the options necessary to reach a web server from the command line. It has switches to specify a username, password, domain, and proxy server. To get a complete list of options, run the utility with the /? switch. Unlike SoapSuds , the output can be generated in VB by entering the following code at the command line:
wsdl /language:VB /out:ZipServiceProxy.vb http://192.168.1.100/ZipService/ZipService.asmx?wsdl
This example produces a file called ZipServiceProxy.vb that contains a class called ZipService . But instead of one method, this version has three: IsValid , BeginIsValid , and EndIsValid . For every web method exposed in the class, two additional methods, with the Begin <webmethod> and End <webmethod> format, are generated in the proxy.
These methods are provided for calling the method asynchronously (a nonblocking call). While using these methods with the zip validation service is unnecessary, an asynchronous client example will be presented anyway. First, though, look at Example 10-2, which contains a synchronous version of the client. It's pretty much a nonevent; it's not even possible to tell that a web service is called by looking at the code.
'vbc /t:exe /r:system.dll /r:system.web.services.dll '/r:system.xml.dll zipclient.vb zipserviceproxy.vb Imports System Public Class Test Public Shared Sub Main( ) Dim zs As New ZipService( ) Dim result As Boolean = zs.IsValid("Houston", "77006") Console.WriteLine(result) End Sub End Class
Example 10-3 is a little more involved, since it contains the listing for an asynchronous client. The code is similar, in many ways, to the War of the Worlds server that was presented in Chapter 8 (see Example 8-17). When the client is initially run, a new thread is created, along with an instance of ZipServiceHandler .
'vbc /t:exe /r:system.dll /r:system.web.services.dll '/r:system.xml.dll zipclient.vb zipserviceproxy.vb Imports System Imports System.Threading Public Class ZipServiceHandler Public Sub New( ) Dim zs As ZipService = New ZipService( ) Dim callback As New AsyncCallback(AddressOf ZipServiceCallback) Dim ar As IAsyncResult = _ zs.BeginIsValid("Houston", "77006", callback, zs) While (ar.IsCompleted = False) 'Do work here while service is running End While End Sub Private Sub ZipServiceCallback(ByVal ar As IAsyncResult) Dim zs As ZipService = CType(ar.AsyncState, ZipService) 'Write the results to the console Console.WriteLine(zs.EndIsValid(ar)) Console.WriteLine("Press ENTER to continue...") End Sub End Class Public Class Client Public Shared Sub Main( ) Dim t As New Thread(New ThreadStart(AddressOf ThreadProc)) t.Start( ) Console.ReadLine( ) End Sub Private Shared Sub ThreadProc( ) Dim zipHandler As New ZipServiceHandler( ) End Sub End Class
In the constructor of ZipServiceHandler , an instance of the web service is created first, followed by an AsyncCallback delegate. The callback delegate refers to the method that is called once the web service finishes executing. A client calling a web service and using an asynchronous callback is different from the War of the Worlds server in Chapter 8, in that data is not sent in chunks when calling a web service asynchronously. There is only one SOAP request and one SOAP response. The proxy class uses a different thread than the client to handle the SOAP response.
Next, BeginIsValid is invoked. The Begin <webmethod> methods have the same parameters as the original web method. Additionally, two parameters are appended to the signature. The first is the callback delegate, and the second is an Object parameter that can pass information to the callback method. In the example, the web service class is passed to ZipServiceCallback in this way. The call returns an IAsyncResult interface that can determine when the web method has completed. Additional processing can occur while waiting for the web method to return.
The callback function takes an IAsyncResult as its only parameter. IAsyncResult.AsyncState is called to retrieve the instance of the web service that was passed during the BeginIsValid call. Processing is stopped by calling EndIsValid and passing it the incoming IAsyncResult interface.
|only for RuBoard|