Building Distributed Apps? Use XML Web Services, Not Remoting (Mostly), Page 2
When you run a Web service, VS.NET creates a default Web page. This page, generally just for testing purposes, offers a simple interface for adding parameters and invoking the Web methods. To test the Web service:
- Click the hyperlinks. You'll see one for each WebMethod (see Figure 1).
- Provide any parameters, if required.
- Click the Invoke button (see Figure 2).
Figure 1: Each WebMethod is listed in the generated test page.
Figure 2: Click the Invoke method to test the selected WebMethod.
The result of invoking a Web method using the test interface is XML (see Figure 3). (This should be your first clue that Web services are not meant to be used in this manner, only tested.)
Figure 3: When you invoke a Web method using the test page, XML is returned.
I hope it is clear from the example that all a Web service and its Web methods are is a class and methods that convert the values passed to and from into XML text. The reason for this is that text and the World Wide Web are the oldest and best of friends. Text is passed easily across the Internet and XML makes that text self-describing.
If programmers had to write parsers to decipher all of the XML mumbo jumbo, XML Web services would represent only a partial solution. However, the .NET framework knows how to easily produce and consume XML Web services and convert XML back into usable code elements. This means that, in practice, you just write code to produce and consume XML Web services and leave the hard part of serialization and HTTP to the .NET Framework.
Consuming a Web Service
When you want to consume a Web service, you actually use some pretty advanced technologies. You use UDDI (Universal Discovery and Description Indexes), WSDL (whizdahl, or Web services Discover Language), and SPROXY. UDDI is a directory service that helps identify Web services. WSDL helps figure out what each service offers, and SPROXY uses the CodeDOM to generate proxy classes, which in turn makes it easy to consume the services a Web service offers. Collectively, these technologies mean all you as a Web service consumer have to do is write code to create instances of the classes and invoke methods offered by the service.
To consume the sample Web service with the Hello World Web method, take the following steps:
- Add any kind of project to a new solution or the solution containing the sample Web service project. It doesn't matter which, but I added a console application to the same solution.
- Select the new project, and then select Project|Add Web Reference. This will open the Add Web Reference dialog (see Figure 4). This is your key to finding Web services on your PC or anywhere else.
- Select Web services on the local machine. If you want a Web service produced by someone else, select the UDDI Directory option (see Figure 4).
- Selecting the first option results in a search for .asmx files on the IIS Server on your PC. (I haven't tried this with the Apache Web server, but one would hope the result is the same.)
- From the list of services, pick the one you just created and click the Add Reference button (see Figure 5).
Figure 4: The Add Web Reference dialog helps you find local Web services or those anywhere in the world.
Figure 5: Add a reference to the desired Web service.
After the last step, you are ready to invoke the Web methods exposed by this service.
Essentially, adding a Web reference means that WSDL, in conjunction with SPROXY, will generate some code. This code will include a namespace and a wrapper class. The wrapper class will condense the code needed to invoke each Web method into a simple method call. SPROXY also uses Reflection to create local versions of composite returned types. For example, if the consumed Web method returns a Customer class, SPROXY will generate a flattened, properties-only version of that class. In generated types, properties are flattened into public fields and other elements are removed. What you end up with is basically a structure, but types defined by the Web services producer also are effectively available in your consumer code. Without the proxy classes, your code would not recognize types defined by the Web service and would not compile.
Some developers mistakenly deploy the assemblies that contain the description of objects returned by Web methods to client machines. This is a critical mistake. By deploying the assemblies on client machines, customers can use free tools such as Anakrino or Reflector to disassemble and decompile these assemblies and learn everything about your business. Don't distribute these assemblies. Instead, permit consumers—whether internal or external—to see only the SPROXY-generated composite types. The effect is two-fold: Consumers can't disassemble your business code and producers are reminded to create Web services that provide macro-level behaviors that satisfy consumer needs.
Invoking an Imported Web Method
After importing a Web service, invoking its Web methods is the same whether the Web method accepts or returns values and whether or not these values are simple or composite types. Number and complexity do not matter at this point. From the consumer's perspective, it is just a matter of creating objects and calling methods.
Note: If you are writing software for an internal customer and performance is an issue, you might want to bypass Web services altogether. Because optimization is a task best performed near the end of development, Web services are better suited to external or distributed consumers as opposed to internal, local consumers.
As is true with any namespace and class, you need to have access to the assembly and its namespaces before you can use what it offers. The namespace for a Web service is generally some variation on the host name. For example, if you have IIS installed on your workstation, create a Web service on your workstation and import that Web service. WSDL/SPROXY will generate a namespace such as localhost or localhost(n). These variations in name are used because WSDL/SPROXY creates a unique folder in your project for the Web service, and this folder contains .map, .vb, .disco, and .wsdl files.
Files with a .map extension indicate the name and location of all of the related files. The .vb file is actually the generated source code. (Click the Show All Files toolbar button in the Server Explorer to see the .vb source code file.) The .vb source file contains wrappers for synchronous and asynchronous calls to the Web service and generated definitions for composite types if they are relevant to the Web service. You can explore the .vb file and even modify it if you like, but generally that is not wise. Listing 2 shows the generated source code for the referenced HelloWorld Web method.