Excerpted with permission from The Book of VB .NET, by Matthew MacDonald, No Starch Press, Copyright 2002.
Consuming a Web Service
At this point, you’ve seen your Web Service in action, but you haven’t usefullyincorporated it into another program. Clearly, if users had to rely on the InternetExplorer test page, they would be better off with a full-featured Internetapplication!
In this section you’ll learn how to create a Web Service client in VB .NET.Best of all, you’ll learn how to get Visual Basic .NET to create all the infrastructurecode you need.
The Proxy Class
Web Service clients communicate with Web Services through a proxy class thatyou can create automatically with the .NET framework. The proxy class translatesyour actions into the appropriate SOAP calls, and then interacts with theWeb Service. The whole process is seamless, so you might not have even realizedit was happening if you hadn’t seen the following diagram:
Figure 13-5: Web Service interaction
Adding a Web Reference
To create the proxy class, you need to add a web reference in Visual Basic .NET.First, add a new Windows application project to the current solution by rightclickingon the Solution Explorer and selecting Add New Project. This projectshould not be created in the virtual directory where the Web Service is locatedbecause it will not be hosted on the web server.
Then, right- click on the new project and select Set As StartUpProject. Now,right-click one more time, and choose Add Web Reference. The Add Web Referencewindow will appear (Figure 13- 6).
Figure 13- 6: Adding a Web Reference
To add a web reference, you need to supply a discovery file. A discovery fileis the first step in the interaction process; it’s where an application “discovers”what Web Services are available, what methods they have, and what informationthey require and return.
You may have noticed that Visual Basic .NET automatically added a .vsdiscofile to your Web Service project. This is the file you now need to select. Clickthe Web References on Local Server link. On the right, a list will appear of thediscovery files residing on your current computer. If you have already created anASP.NET application, you may find more than one discovery file. Choose theone that is in the appropriate directory.
Once you have made your selection, information will appear about the WebServices specified by the discovery document you’ve chosen. You can click onthe View Contract button to see all the WSDL information again, or click on theAdd Reference button to link to this Web Service and generate the requiredproxy class automatically.
Inspecting the Proxy Class
Visual Basic .NET hides the proxy class it creates from you, because you don’treally need to see it or modify it directly. However, it’s always a good idea topeek under the hood of an application and get a better understanding of what’sreally happening along the way.
To see the proxy class, select Project Show All Files from the menu. Then,look for the folder under Web References with the .WSDL file. Double- click toexpand this entry, and you’ll see the Visual Basic proxy file (typically calledReference.vb), as shown in Figure 13-7.
Figure 13- 7: The hidden proxy class
This file includes all the methods from your WebService, in slightly modifiedform. It also includes the ClientPackageInfo class that you need to use toretrieve the information from the GetPackageInfo method. Interestingly, VisualBasic .NET is intelligent enough to leave out the Package class, so the client willhave no idea that the Web Service uses this class internally and won’t be able tosnoop out its internal details.
Take a quick look at the modified version of the GetPackageInfo function containedin the proxy class. (The attributes that precede it are not included here.)
Public Function GetPackageInfo(ByVal TrackID As String) As ClientPackageInfo Dim results() As Object = Me.Invoke("GetPackageInfo", New Object() {TrackID}) Return CType(results(0), ClientPackageInfo)End Function
This function converts the TrackID input string into a generic object, andretrieves the result as a generic object, which is then converted into the appropriateClientPackageInfo object. In between, it accesses the Web Service through theappropriate SOAP request and waits for the response, although all this is takencare of automatically in the Me.Invoke method, with the help of the .NET attributesthat provide additional information to the Common Language Runtime.
Another interesting aspect of the proxy class is the constructor, which setsthe URL. If you change the location of your Web Service, you can modify thismethod, rather than regenerating the proxy class. Notice, however, thatlocalhost is specified, regardless of the name of your computer. Localhost is a”loopback” alias that always points to the current computer.
Public Sub New() MyBase.New Me.Url = "http://localhost/WebService/PostalWebService.asmx"End Sub
Using the Proxy Class
The proxy class is the key to using a Web Service. Essentially, you create aninstance of this class, and call the appropriate methods on it. You treat theproxy class as though it were a local class that contained the same functions andmethods as the Web Service.
Add the following code to the Load event of your startup Windows Form:
Dim ServiceInstance As New localhost.PostalWebService()Dim PackageInfo As New localhost.ClientPackageInfo()PackageInfo = ServiceInstance.GetPackageInfo("221")MessageBox.Show("Received the delivery date: " & PackageInfo.DeliveryDate)
Now run the application. If Visual Studio .NET loads up the Web Serviceweb page, you have to stop the project and set the Windows application as thestartup project, using the right-click menu in the Solution Explorer window.If you have configured everything correctly, you’ll see the window inFigure 13- 8.
Figure 13- 8: The Web Service results
Once again, the technical details are pretty sophisticated, but the actualimplementation is hidden by the .NET framework. Calling a Web Service is aseasy as creating one, once you have set up the web reference.
Debugging a Web Service Project
When debugging a solution that includes a Web Service project and client, youwill find that any breakpoints or watches you set for the Web Service code areignored. That is because, by default, Visual Studio .NET only loads the debugsymbols for the startup project, which is the client.
To solve this problem, you need to configure Visual Studio .NET to loadboth projects at once. Right- click on the solution item in the Solution Explorer,and select Properties. Then, browse to the Common Properties / StartupProject tab, and specify Multiple Startup Projects, so that both your client andthe Web Service will be built when you click the start button (Figure 13- 9).
Figure 13-9: Starting multiple projects
You still need to make one more change. By default, Visual Studio .NETstarts the Web Service project by displaying the Internet Explorer test page. Inthis case, however, you don’t want any action to be taken other than loading thedebug symbols so that the Web Service code is available for debugging.
Right- click on the Web Service project, and select properties. In the ConfigurationProperties / Debugging tab, select “Wait for an external process to connect”as your start action (Figure 13-10). You can now use the full complementof debugging tools with your Web Service and client code.
Figure 13-10: Loading the Web Service debug symbols
Asynchronous Web Service Calls
You may have noticed that the proxy class actually contains more than just theGetPackageInfo function. It also includes BeginGetPackageInfo and EndGet-PackageInfo procedures. These routines allow you to retrieve a Web Serviceresult asynchronously. For example, your code can submit a request with Begin-GetPackageInfo, perform some additional tasks, and then retrieve the resultwith EndGetPackageInfo. This allows your program to remain responsive, evenwhen waiting for a response over a slow Internet connection.
Dim ServiceInstance As New localhost.PostalWebService()Dim PackageInfo As New localhost.ClientPackageInfo()
' Create a special handle that will allow us to retrieve the result.Dim ResultHandle As IAsyncResult
' Submit the request.ResultHandle = ServiceInstance.BeginGetPackageInfo("221", Nothing, Nothing)
' (Perform other time-consuming tasks.)
' Retrieve the final result, using the ResultHandle.PackageInfo = ServiceInstance.EndGetPackageInfo(ResultHandle)
MessageBox.Show("Received the delivery date: " & PackageInfo.DeliveryDate)
In this example, ResultHandle is a special IAsyncResult object that tracksthe current request. You can have multiple asynchronous web requests submittedat the same, as long as you keep track of them with different IAsyncResultobjects.
When the EndGetPackageInfo method is called, the request becomes synchronous.That means that if the response has not yet been received from theWeb Service, your code will wait until it is received (or until the request timesout, according to the proxy class Timeout property).
Alternatively, you can call BeginGetPackageInfo with the address of a subroutinein your program. When the Web Service result is received, this routinewill be called automatically. This technique is ideal if you are requesting someinformation that is not critical, and you want to allow your application to continueits normal behavior. By using a callback, you can respond immediatelywhen the information arrives, but your application doesn’t need to sit idle waitingfor it.
Here’s an example where a button click submits an asynchronous requestwith a callback:
Private ServiceInstance As New localhost.PostalWebService()
Private Sub cmdAsyncCallback_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdAsyncCallback.Click ' Create a special handle that will allow us to retrieve the result. Dim ResultHandle As IAsyncResult
' Submit the request, with a callback. ServiceInstance.BeginGetPackageInfo("221", AddressOf ResultReceived, Nothing)End Sub
When the result is received, the ResultReceived subroutine will be calledimmediately, and a MessageBox will appear alerting the user. (A more commonaction might be to use the information to update a portion of the user interface.)The PostalClient included with the online samples demonstrates these differentways of retrieving information from a Web Service asynchronously.
Private Sub ResultReceived(ByVal ar As IAsyncResult) ' Retrieve the final result, using the ar event arguments.. Dim PackageInfo As New localhost.ClientPackageInfo() PackageInfo = ServiceInstance.EndGetPackageInfo(ar)
MessageBox.Show("Received the delivery date: " & PackageInfo.DeliveryDate)End Sub
TIP You could also use the threading techniques described in Chapter 10 of The Book of VB .NET. For example, you could create a procedure that calls a series of web methods on a separate thread and then raises an event to notify your program.
Web Service Discovery
One aspect of this process that I’ve glossed over a bit is the discovery file. Thediscovery file represents Microsoft’s goals for easily sharing Web Services andmaking them available to the appropriate clients. You don’t need to worry aboutthese sharing issues if you are creating a Web Service exclusively for use in yourcompany website, or with a few select clients. However, if you are trying todevelop and market a Web Service that provides powerful features that you wantto provide as a subscription service, discovery matters quite a lot.
Discovery is the process that allows a client to find the WSDL informationthat describes your Web Service. Your server may have one discovery documentthat points to multiple Web Services, or it may have several different discoveryfiles in various virtual directories. Alternatively, you might not use a discoveryfile at all.
A discovery document is yet another type of XML file. All it contains is a listof web links (URLs) to the WSDL document of various Web Services. Here is asample discovery file for our PostalWebService:
<?xml version="1.0" encoding="utf-8" ?><disco:discovery xmlns_disco="http://schemas.xmlsoap.org/disco" xmlns:wsdl="http://schemas.xmlsoap.org/disco/wsdl"> <wsdl:contractRef ref="http://fariamat/NoStarchWeb/PostalWebService.asmx?WSDL"</disco:discovery>
By default, discovery files use dynamic discovery, and look a little different.With dynamic discovery, every subdirectory in the current directory will bescanned for Web Services. Here is a sample dynamic discovery file:
<?xml version="1.0" encoding="utf-8" ?><dynamicDiscovery > <exclude path="_vti_cnf" /> <exclude path="_vti_pvt" /> <exclude path="_vti_log" /> <exclude path="_vti_script" /> <exclude path="_vti_txt" /> <exclude path="Web References" /></dynamicDiscovery>
The exclude path attributes tell the discovery process not to look in thosefolders. This can be used to save time (for example, by restricting directoriesthat contain only images), or to restrict a Web Service from appearing in a discoveryprocess (although it will still be accessible unless you have specificallywritten authentication or security code).
TIP How can you add a web reference if you don’t have a discovery file? You can manuallytype the Web Service location into the address bar of the Add Reference window. Youdon’t even need to worry about supplying the ?WSDL at the end to locate the WSDLdocument, because Visual Studio .NET is smart enough to add that for youautomatically.
What Comes Next?
This article has provided an overview of how Web Services work, and how touse them. Leading edge companies and developers have already started inventingall kinds of imaginative Web Services. Some examples include Microsoft’s Passport,which allows other companies to provide authentication using the enginethat powers the Hotmail email system, and CodeSwap (www.vscodeswap.net),a Microsoft-supported initiative that allows you to share code fragments withother developers as easily as you could once share MP3 files in the originalNapster program.
If you want to continue learning about and working with Web Services, hereare some interesting places to start:
- Microsoft provides a Web Services portal that provides such information aslow-level technical information about the SOAP and WSDL standards, codesamples of professional Web Services, and white papers discussing the bestways to design Web Services. Check it out at http://msdn.microsoft.com/webservices.
- UDDI (Universal Description, Discovery, and Integration) is an emergingstandard that will make it easy for developers to locate discovery files andavailable Web Services on the Internet. You can find more information, andsome interesting examples of Web Services, at http://uddi.microsoft.com/visualstudio.
- Various other Web Service search engines are appearing on the Internet.You can use them to see what other developers are up to. To getstarted, try out http://www.gotdotnet.com/playground/services andhttp://www.xmethods.com.
- Remember, you can enable Session support in a Web Service, and use someof the same techniques that you would use for authentication in an ASP.NETapplication. For more information, check out the MSDN help library.
Matthew MacDonald, an author, educator and MCSD developer, has worked with Visual Basic and ASP since their inceptions. He is the author of “The Book of VB .NET” (No Starch Press) and “ASP.NET: The Complete Reference” (Osborne McGraw-Hill) and co-author of “Programming .NET Web Services” (O’Reilly).