Many of the early Web services products are heavily oriented to the server side of the transaction. Microsoft .NET is different in this regard in that it provides considerable support to the client, or consuming side, of the task at hand.
Web services are created in such a way that any SOAP-compliant client can access them. There are some mechanical considerations to deal with, however, when you start creating a client. This hour shows you how .NET languages can use the framework as a foundation for creating clients that consume the Web services that we write. In addition, you will create several more sophisticated Web services and the clients that communicate with them. You will learn specifically how to
Create a client that requests data.
Return a structure from a Web service.
Create a Web services client.
Create a client to access a Web service from another company.
You could write a program that generates SOAP messages by concatenating characters together one at the time. Although this would be possible to achieve, it would be tedious and unnecessary for most applications. It is far easier to use a combination of a tool and the WSDL document for service to generate the code for that client. Even better than that is the .NET approach in which the framework can dynamically read the WSDL in order to create the correct SOAP messages. In this section, we will step through the process of creating a simple Web service and a client that can access it.
The first example that we will work will demonstrate how user-defined data types, sometimes called structures or objects, can be passed from a Web service to a client. We can define a simple structure within a Web service as shown in Listing 16.1.
Example 16.1. The StructService1.asmx File
Imports System.Web.Services <WebService(Namespace:="http://www.samspublishing.com")> _ Public Class StructService1 Inherits System.Web.Services.WebService #Region " Web Services Designer Generated Code " Public Structure Employee Dim Name As String Dim Age As Integer Dim SSN As String Dim HDate As Date End Structure <WebMethod()> Public Function GetEmployee() As Employee Dim emp As Employee emp.Name = "John Libby" emp.Age = 48 emp.SSN = "234-56-7890" emp.HDate = #1/6/1955# Return (emp) End Function End Class
You will recognize much of this code from the examples that we worked in the last hour. The unique part of this example is the definition of a structure inside the Web service file.
Public Structure Employee Dim Name As String Dim Age As Integer Dim SSN As String Dim HDate As Date End Structure
We will create a Web method to send this structure to a client. Notice that we don’t send it as individual data items, but as a unit.
<WebMethod()> Public Function GetEmployee() As Employee Dim emp As Employee emp.Name = "Steve Potts" emp.Age = 48 emp.SSN = "234-56-7890" emp.HDate = #1/6/1955# Return (emp) End Function
You can find the WSDL for a Web service in the Web References section of the Solution Explorer pane of the IDE. The WSDL declaration that .NET has generated for this Web service contains some interesting features, as shown here:
<types> <s:schema elementFormDefault="qualified" targetNamespace="http://www.samspublishing.com"> <s:element name="GetEmployee"> <s:complexType /> </s:element> <s:element name="GetEmployeeResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="GetEmployeeResult" type="s0:Employee" /> </s:sequence> </s:complexType> </s:element> <s:complexType name="Employee"> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="Age" type="s:int" /> <s:element minOccurs="0" maxOccurs="1" name="SSN" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="HDate" type="s:dateTime" /> </s:sequence> </s:complexType> <s:element name="Employee" type="s0:Employee" /> </s:schema> </types>
Notice that the GetEmployeeResponse
element contains a complexType
.
<s:element name="GetEmployeeResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="GetEmployeeResult" type="s0:Employee" />
The complexType
is composed of simple types.
<s:complexType name="Employee"> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="Age" type="s:int" /> <s:element minOccurs="0" maxOccurs="1" name="SSN" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="HDate" type="s:dateTime" /> </s:sequence> </s:complexType>
Notice that each element in the complexType
represents a field in the structure that we want to convey.
The messages are defined here:
<message name="GetEmployeeSoapIn"> <part name="parameters" element="s0:GetEmployee" /> </message> <message name="GetEmployeeSoapOut"> <part name="parameters" element="s0:GetEmployeeResponse" /> </message>
The bindings for SOAP are shown here:
<binding name="StructService1Soap" type="s0:StructService1Soap"> <soap:binding transport= "http://schemas.xmlsoap.org/soap/http" style="document" /> <operation name="GetEmployee"> <soap:operation soapAction= "http://www.samspublishing.com/GetEmployee" style="document" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding>
The WSDL service element is defined, which contains the URL of the Web service.
<service name="StructService1"> <port name="StructService1Soap" binding="s0:StructService1Soap"> <soap:address location= "http://localhost/StructService/StructService1.asmx" /> </port>
Creating the client for the StructService1 Web service is easy to do using Visual Basic .NET. The first step is to create a new Windows Application project that contains a form. Using the toolbox, add four text boxes called txtName, txtAge, txtSSN, and txtHireDate to the form. The code for this form is shown in Listing 16.2.
Example 16.2. The Form1.vb File
Public Class Form1 Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " #End Region Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim WS As New localhost.StructService1() Dim employee1 As localhost.Employee employee1 = WS.GetEmployee() txtName.Text = employee1.Name txtAge.Text = employee1.Age txtSSN.Text = employee1.SSN txtHireDate.Text = employee1.HDate End Sub End Class
We chose the Form1_Load
event procedure to populate the form with information retrieved from the StructService1 Web service. The first thing that we must do is make the connection with the service. The WSDL file becomes part of the client project when you add the Web reference to the project. Until then, the name StructService1()
will be meaningless.
Dim WS As New localhost.StructService1()
The Employee
type is known because it exists in the WSDL.
Dim employee1 As localhost.Employee
The call to retrieve the employee is made using the handle to the Web service class.
employee1 = WS.GetEmployee()
We display the values on our form by referencing the employee’s information using the Visual Basic dot notation. The text box’s Text
property controls what is displayed. By assigning a value to it, you cause it to be displayed on the screen.
txtName.Text = employee1.Name txtAge.Text = employee1.Age txtSSN.Text = employee1.SSN TxtHireDate.Text = employee1.HDate
Adding the Web reference is the only complicated part of this example. The easiest way to do this is to choose Add Web Reference from the Project Menu. The Add Web Reference dialog box will appear as shown in Figure 16.1.
Now, all that is left is to run the StructClient application by pressing the F5 key or by choosing Start from the Debug menu. The result is shown in Figure 16.2.
Compared to the complicated process required by some tools, the Visual Basic .NET approach is very simple to learn and use.
Up to this point, we have limited our examples to Web services that we have created. Much of the development that you will do in your career will consume Web services written by others. You can call the methods that those services expose almost as easily as you can call methods on the services running on your own machine.
The Visual Studio IDE provides a built-in way to find Web services to communicate with: the Add Web Reference dialog box. A Web reference is a reference to an object that is located somewhere on the Web. When we add a Web reference to a project, we are asking .NET to add the “plumbing” needed for your application to make requests of this remote reference and to get responses from them. To do this, create a new Windows Application project. When the skeleton project appears in the Solution Explorer, right-click on the project name and choose Add Web Reference from the context menu. When the dialog box appears, click on the UDDI Directory hyperlink.
This will bring up the dialog box shown in Figure 16.3.
Type "IBM"
in the Provider Name text box, and then click the Search button. This will retrieve a number of possible selections as shown in Figure 16.4.
After some frustration with the fact that we couldn’t find any Web services that provided decent references, we decided to copy the URL from one of these entries as shown here:
http://dwdemos.alphaworks.ibm.com/IBM_WSI_Sample/
We copied and pasted this link into the address bar of our dialog and pressed Enter. The following page appeared in the Add Web Reference dialog box as shown in Figure 16.5.
Next, we chose the View the List of the Deployed Web Services hyperlink, which caused the screen shown in Figure 16.6 to appear.
Because we wanted a simple Web service for this example, we chose the first one, the Version Web service. This caused the dialog box to display the WSDL for that service to appear in the left panel, as shown in Figure 16.7.
We clicked on the Add Reference button at the bottom of this dialog box to add this WSDL to our project. Figure 16.8 shows the Solution Explorer window with the Version.wsdl file listed under the Web References tree.
The WSDL document itself is fairly easy to understand. Listing 16.3 shows what it looks like.
Example 16.3. The Version.wsdl File
<?xml version="1.0" encoding="utf-8"?> <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://dwdemos.alphaworks.ibm.com/IBM_WSI_Sample/services/Version" xmlns:s=http://www.w3.org/2001/XMLSchema xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" targetNamespace= "http://dwdemos.alphaworks.ibm.com/IBM_WSI_Sample/services/Version" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types /> <message name="getVersionResponse"> <part name="getVersionReturn" type="s:string" /> </message> <message name="getVersionRequest" /> <portType name="Version"> <operation name="getVersion"> <input name="getVersionRequest" message="tns:getVersionRequest" /> <output name="getVersionResponse" message="tns:getVersionResponse" /> </operation> </portType> <binding name="VersionSoapBinding" type="tns:Version"> <soap:binding transport=http://schemas.xmlsoap.org/soap/http style="rpc" /> <operation name="getVersion"> <soap:operation soapAction="" /> <input name="getVersionRequest"> <soap:body use="encoded" namespace= "http://dwdemos.alphaworks.ibm.com/IBM_WSI_Sample/services/Version" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" /> </input> <output name="getVersionResponse"> <soap:body use="encoded" namespace= "http://dwdemos.alphaworks.ibm.com/IBM_WSI_Sample/services/Version" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" /> </output> </operation> </binding> <service name="VersionService"> <port name="Version" binding="tns:VersionSoapBinding"> <soap:address location= "http://dwdemos.alphaworks.ibm.com/IBM_WSI_Sample/services/Version" /> </port> </service> </definitions>
The binding tells us how to communicate with this service. We are going to use SOAP and the rpc style on an operation (method) called getVersion
.
<binding name="VersionSoapBinding" type="tns:Version"> <soap:binding transport=http://schemas.xmlsoap.org/soap/http style="rpc" /> <operation name="getVersion"> <soap:operation soapAction="" /> <input name="getVersionRequest"> <soap:body use="encoded"
The address that we will use to access the SOAP version of this Web service is shown here:
<port name="Version" binding="tns:VersionSoapBinding"> <soap:address location= "http://dwdemos.alphaworks.ibm.com/IBM_WSI_Sample/services/Version" /> </port>
The information in this document is sufficient for .NET to communicate with this Web service directly using SOAP.
Now that we have a Web reference to a service on the IBM site, we can try and make calls to it. The code in Listing 16.4 gives us a client to test it with.
Example 16.4. The Version’s Form1
Class
Public Class Form1 Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim WS As New com.ibm.alphaworks.dwdemos.VersionService() Dim s1 As String s1 = WS.getVersion() txtVersionInfo.Text = s1 End Sub End Class
Add a button, Button1, and a text box, txtVersionInfo, to the form and place this code in the Form1
class. Access to the Web service is provided by the fact that the Web reference for this service has been added to the project and the following line of code has been included:
Dim WS As New com.ibm.alphaworks.dwdemos.VersionService()
Here, we are creating a handle to an object located on another computer somewhere on the Internet. We use that handle to make a call to the method of that service.
s1 = WS.getVersion()
The return value from this is a String
, so we will assign it directly to the Text
property of a text box.
txtVersionInfo.Text = s1
You can run this example by choosing Start from the Debug menu or pressing the F5 key. After the window appears, click on the Get Version button. Figure 16.9 shows what the result looks like.
Notice that we are using a .NET client to talk to an Apache Axis Web service, without any special effort on our part. The WSDL provides all the information that we need to communicate. Notice also that we have no information about the language that the Version Web service was written in, nor do we need to know it. The SOAP engine on the server translates the SOAP messages into the programming language of the server in the same way that the CLR translates the SOAP messages into Visual Basic for us.
In this hour, you learned how to create Web service clients using Visual Basic and the .NET platform. You first learned how to create a client that could make a request and receive a complex data type.
After that, you learned how to use Visual Studio to create a client to connect to a Web service that we found by searching directory services.
The .NET approach to creating clients is by far the most advanced of any on the market. It will be interesting to see whether the current Web service development community will be open to a Microsoft solution, given that so many of them sided with Sun Microsystems in the “Java Wars” of the past decade.
The Workshop is designed to help you review what you’ve learned and begin learning how to put your knowledge into practice.