Using the SOAP Toolkit on the Server

In this section, we will recreate the example server shown in Chapter 1. This will involve a bit more work than what we saw to build the example using ASP.NET. The SOAP Toolkit does not know how to serialize or deserialize properties contained in an object. Instead, you need to handle this transformation yourself. For passing arrays of complex types, you will need to handle the complete transformation yourself. We will look at this in detail. If you pass simple data back and forth, things are quite a bit easier.

The code to handle the first three functions (HelloWorld, GetRandomNumbers, and GetPerson) is pretty basic, and is shown in Listing 10.1. The infrastructure included with the SOAP Toolkit handles these three with minimal interaction from you.

Listing 10.1. Code to implement HelloWorld, GetRandomNumbers, and GetPerson
Public Function HelloWorld() As String
    HelloWorld = "Hello World"
End Function

Public Function GetRandomNumbers(ByVal arraySize As Long, _
    ByVal min As Long, ByVal max As Long) As Long()

    Dim i As Long
    Dim retval() As Long
    ReDim retval(1 To arraySize) As Long
    For i = 1 To arraySize
        Randomize
        retval(i) = CLng((max - min + 1) * Rnd + min)
    Next i
    GetRandomNumbers = retval
End Function

Public Function GetPerson() As Person
    Dim retval As New Person
    Dim aName As New Name
    retval.Birthday = "April 5, 1972"
    aName.First = "Scott"
    aName.Middle = "Christopher"
    aName.Last = "Seely"
    retval.theName = aName
    Set GetPerson = retval
End Function
					

The Person and Name objects follow the same general layout as they did in the first chapter. Person contains a Name and a Date. Name contains three strings. Both use Property Get/Let to change the values. Things get a lot more complicated with GetAuthorNames. As a result of this, this section will have more text focused on handling the difficult case and little text that handles the simple cases. For reference, to the client, it needs to look like the GetAuthorNames function has the following signature:

Public Function GetAuthorNames() as Name()

This Web Service is being built under the assumption that it is intended to be a Web Service and not just another COM object. Using the SOAP Toolkit, you can choose to simply expose a COM object as a Web Service. This statement is important when looking at the code that makes GetPerson and GetAuthorNames work.

Generating the WSDL and WSML files

The SOAP Toolkit includes a utility, wsdlgen.exe, that allows you to create the WSDL and WSML files. We covered WSDL in Chapter 3, “SOAP, WSDL, and UDDI Explained.” WSML stands for Web Services Meta Language. It is a proprietary XML vocabulary that the SOAP toolkit uses to map WSDL information to its COM counterpart. We will look at the details of the WSML and WSDL files as we progress. They will need to be edited by hand before we are done.

Before we go much further, make sure you have the Ch10STKExSvr project from the companion Web site for this book. You will find it helpful to have this project on your machine so that you can follow along with this discussion.

wsdlgen.exe, also known as the WSDL Generator, will get everything started. For two of the operations, GetRandomNumbers and HelloWorld, the WSDL Generator will be all we need to expose these operations as Web Services. The tool has both a command line and GUI. We will focus on the GUI. The command-line use comes in handy when adding creation of the WSDL and WSML files to the end of any build scripts you may create. All features available from the GUI are available through the command line.

When using the SOAP Toolkit to create your Web Service, you will first want to do the following things in the Visual Basic 6.0 project:

1.
Create your ActiveX DLL project and add any classes you want to expose as a Web Service.

2.
For the classes you will expose, type in stubs defining the Web Method signature. You will have plenty of time to actually implement the methods.

3.
Build the project. Rename the resulting binary to something like [project name]_baseline.dll.

4.
In the project settings, make sure you have everything set up for binary compatibility. From within Visual Basic 6.0, select Project, [Project Name] Properties. Switch to the Component tab and select the Binary Compatibility radio button in the Version Compatibility group box. Then, select the DLL you renamed in the previous step as the file with which you will be compatible.

The last step is very important. The SOAP Toolkit uses the object's IDispatch interface to call the functions exposed by the COM object. It stores the ID of each function in the WSML file. If you do not set Binary Compatibility and choose a file that contains a type library, the IDs of the functions will change every time you rebuild the library. If you want to make things even more stable, you have the option of writing an IDL file and compiling it into a type library. If you have written IDL files in the past, this option may be better because it locks the interface. The downside to this is that IDL files are harder to create than just updating the WSDL and WSML files as needed.

After you have the stubbed out version in place and a stable type library (either as a .tlb or .dll), you will be ready to run the WSDL Generator. To handle the intended deployment, you should go ahead and create a virtual directory named Ch10STKExSvr on your local machine. You can do this by using inetmgr.exe and mapping the virtual directory to the same spot in which the DLL is sitting. Alternatively, you can create the directory in the inetpubwwwroot directory. To mark the directory as a Web application, open up inetmgr and navigate to (local computer) Web SitesDefault Web SiteCh10STKExSvr and right-click the node. Select the Properties option from the pop-up menu. On the Directory tab under Application Settings, click the Create button to mark the directory as a Web application. Then click OK to save the change. Assuming that you have installed the SOAP Toolkit (available at http://msdn.microsoft.com), select Start, Programs, Microsoft SOAP Toolkit, WSDL Generator. This will bring up the dialog shown in Figure 10.1.

Figure 10.1. Opening dialog for the WSDL Generator.


From this dialog, press Next. Name the service Ch10STKExSvr. For the local path, select the Ch10STKEx.dll file. When you are done, this next screen should be set up as shown in Figure 10.2.

Figure 10.2. Selecting the file that contains the COM objects you want to expose.


Press Next and continue to the next screen. This is where you will select which objects and methods you want to expose on your object. By selecting a parent in the tree, all child nodes are automatically selected. Because the Web Service exposes all items, select the top node. The selection should look as shown in Figure 10.3.

Figure 10.3. Exposing objects and related methods.


Now, you need to tell the WSDL Generator the name of the Web site that will be used to expose the WSDL and WSML files. You can also choose to expose the Web Service using an ISAPI or ASP listener. What's the difference?

The ISAPI listener assumes that you will completely hook into the default behavior of the SOAP Toolkit. All the types you use must be able to serialize and deserialize themselves using the built-in tools or using a fairly simple serializer/deserializer that you build yourself.

If you need to do any special processing or if you need to be able to read and write SOAP messages yourself, you need to use the ASP listener. This listener can be used to handle the default dispatch mechanisms, or you can use it to do your own dispatching. How you do things depends on what you need to do.

You should always create XSD information using the 2001 namespace. This namespace maps to the final XSD specification and is understood by all of the more popular open-source and commercial SOAP toolkits. By the time we are done, we will use both the ISAPI and ASP listeners to illustrate their uses. Figure 10.4 shows the selections for the initial WSDL file.

Figure 10.4. Exposing objects and related methods.


Next, select the default character set and location to place the generated files. The default setting for the WSDL character set, UTF-8, is just fine. You also need to pick a place for the generated files to be placed. I had the toolkit place them in the directory mapped to the Ch10STKExSvr virtual directory, as shown in Figure 10.5.

Figure 10.5. Generating the WSDL and WSML files.


Now press Next, and read any warning messages. You are now done. You should have received a warning message indicating that the SOAP Toolkit did not understand how to serialize some data types and that they will be marked with question marks (?) in the WSDL file. Now that we have generated the WSDL file, we will move on and take a look at some edits that you will need to make to it.

Common Edits to the WSDL and WSML Files

If you commonly pass complex types over your Web Service, get used to editing the WSDL and WSML files by hand. After you get things working, you will not want to use the tool to regenerate the files and lose any of your changes. The common edits include the following:

  • Changing the endpoint

  • Adding complex types to the types section

  • Changing return types

  • Adding portTypes, bindings, and service endpoints

  • Mapping complex types to custom type mappers

The easiest of these items is changing the endpoint. When you initially deploy a Web Service for development on the local machine, it may make sense to set the service endpoint to point at localhost. Doing so allows for easy redeployment to other machines for single machine testing where the client and server run locally. Later, you will want to have the WSDL declare the path to the machine from either the intranet or Internet. Often, clients will keep a copy of the WSDL file locally to avoid an expensive round trip just to load the WSDL file. It will also be helpful for new clients to have a copy of the WSDL with the Web Service's actual endpoint for future reference. Regardless of the use, the edit is fairly easy to do. Open up the WSDL file and scroll to the end. There, you will find the service section. It will look something like the following:

<service name='Ch10STKExSvr' >
  <port name='TheServiceSoapPort' binding='wsdlns:TheServiceSoapBinding' >
    <soap:address location='http://localhost/Ch10STKExSvr/Ch10STKExSvr.WSDL' />
  </port>
</service>

Just go into the soap:address element and change the value of the location attribute to point at the correct endpoint. You can also alter this value to point to a trace utility such as the Microsoft SOAP Toolkit's Trace Utility. (This application was discussed in Chapter 5, “Troubleshooting Web Services/Consumers.”)

We have complex types that declare the Person, Name, and an array of Name objects. These need to be added to the WSDL file. Listing 10.2 shows the types as they are declared by the wizard.

Listing 10.2. The WSDL Generator Defined Types Section
<types>
    <schema targetNamespace='http://tempuri.org/type'
        xmlns='http://www.w3.org/2001/XMLSchema'
        xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'
        xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
        elementFormDefault='qualified'>
        <complexType  name ='ArrayOfint'>
            <complexContent>
                <restriction base='SOAP-ENC:Array'>
                    <attribute ref='SOAP-ENC:arrayType'
                       wsdl:arrayType='int[]'/>
                </restriction>
            </complexContent>
        </complexType>
    </schema>
</types>

This part does not even contain the question mark defined types. That part, shown in Listing 10.3, appears in the two message definitions that make use of the “unknown” types:

Listing 10.3. The Messages Using Undefined Types
<message name='TheService.GetPersonResponse'>
    <part name='Result' type='xsd:???????'/>
</message>
<message name='TheService.GetAuthorNames'>
    <part name='Request' type='xsd:???????'/>
    <part name='Response' type='xsd:???????'/>
</message>
<message name='TheService.GetAuthorNamesResponse'>
</message>

To handle these items, we need to include type definitions. These definitions are not explicitly used by the SOAP Toolkit. Instead, we include them so that clients using the Web Service know how the data will be presented in the returned XML. This information allows the people creating clients to successfully interpret the returned data. Listings 10.4 and 10.5 contain the type definitions and message updates needed to make it easier for clients. This information is added to the types section of the Ch10STKExSvr.WSDL file.

Listing 10.4. Types Needed to Define Name, Person, and Array of Name
<complexType name="Person">
    <sequence>
        <element minOccurs="0" maxOccurs="1" name="theName"
            type="typens:Name" />
        <element minOccurs="1" maxOccurs="1" name="birthDay"
            type="dateTime" />
    </sequence>
</complexType>
<complexType name="Name">
    <sequence>
        <element minOccurs="0" maxOccurs="1" name="First"
            type="string" />
        <element minOccurs="0" maxOccurs="1" name="Middle"
            type="string" />
        <element minOccurs="0" maxOccurs="1" name="Last"
            type="string" />
    </sequence>
</complexType>
<complexType name="ArrayOfName">
    <complexContent mixed="false">
        <restriction base="SOAP-ENC:Array">
            <attribute wsdl:arrayType="typens:Name[]"
                ref="SOAP-ENC:arrayType"
                xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" />
        </restriction>
    </complexContent>
</complexType>
						

Listing 10.5. Updated Messages to Use New XSD Types
<message name='TheService.GetPersonResponse'>
    <part name='Result' type='typens:Person'/>
</message>
<message name='TheService.GetAuthorNames'>
</message>
<message name='TheService.GetAuthorNamesResponse'>
    <part name='Result' type='typens:ArrayOfName' />
</message>

You will notice that the types in the message now reflect the data needed for any clients. The SOAP Toolkit will also use this information to find custom type mappers in the WSML file. You will notice a radical change to the messages used for GetAuthorNames and GetAuthorNamesResponse. The reason is that the user needs to look at the message as though it was produced with no parts in the request and an array of names in the response. The actual signature of the GetAuthorNames function is as follows:

Public Sub GetAuthorNames(ByVal Request As ASPTypeLibrary.Request, _
                   ByVal Response As ASPTypeLibrary.Response)

The two arguments, Request and Response, allow the GetAuthorNames function to directly read the HTTP message and write the HTTP response. Before looking at the hardest example of type mapping, we will look at the easy case. By the way, the two simple functions, GetRandomNumbers and HelloWorld, already work for the Web Service. Now, we are working on hooking up the other two.

We will first focus on adding the custom type mapper for the Person type. A custom type mapper implements the ISoapTypeMapper interface. This allows the type to read and write itself as XML. ISoapTypeMapper defines four functions—Init, varType, read, and write. Init gets called whenever the class gets loaded. In this function, you should create any objects you might need or capture any information tied to the object schema. The varType function has you return the type of variable the serializer handles. Typically, this will be vbObject. The read and write functions simply read and write the object as XML. We will spend our time looking at the write function (in the client side we will investigate the read portion).

The project contains a class called PersonMapper. PersonMapper only knows how to write a Person and the Name contained within. Listing 10.6 contains the code from the PersonMapper class that writes out a Person object.

Listing 10.6. Function to Write a Person out as XML from PersonMapper class.
Private Sub ISoapTypeMapper_write( _
    ByVal pSoapSerializer As MSSOAPLib.ISoapSerializer, _
    ByVal bstrEncoding As String, _
    ByVal encodingMode As MSSOAPLib.enEncodingStyle, _
    ByVal lFlags As Long, _
    pvar As Variant)

    Dim aPerson As Person
    Dim aName As Name
    ' pvar points to an existing Person object
    Set aPerson = pvar

    ' Grab the name embedded in the Person
    Set aName = aPerson.theName

    ' Write out the name first
    pSoapSerializer.startElement "theName"
        pSoapSerializer.startElement "First"

        ' m_stringMapper was captured in the Init function. It knows
        ' how to serialize strings.
        m_stringMapper.write pSoapSerializer, bstrEncoding, _
            encodingMode, lFlags, aName.First
        pSoapSerializer.endElement
        pSoapSerializer.startElement "Middle"
        m_stringMapper.write pSoapSerializer, bstrEncoding, _
            encodingMode, lFlags, aName.Middle
        pSoapSerializer.endElement
        pSoapSerializer.startElement "Last"
        m_stringMapper.write pSoapSerializer, bstrEncoding, _
            encodingMode, lFlags, aName.Last
        pSoapSerializer.endElement
    pSoapSerializer.endElement

    ' Write out the birthday
    pSoapSerializer.startElement "birthDay"

    ' m_dateMapper was captured in the Init function. It knows
    ' how to serialize dates.
    m_dateMapper.write pSoapSerializer, bstrEncoding, encodingMode, _
        lFlags, aPerson.Birthday
    pSoapSerializer.endElement
End Sub
						

To tell the SOAP Toolkit what to do when it needs to convert a Person from XML, a few entries need to be made in the WSML file. First, add the types section. Then, map the type declared there to the PROGID used to instantiate the type mapper. The SOAP Toolkit will handle everything else. Listing 10.7 shows the lines used to map the PersonMapper object to the Person XSD type.

Listing 10.7. Using PersonMapper to Convert Person to XML
<using PROGID='Ch10STKEx.PersonMapper' cachable='0'
    ID='PersonMapperObject' />
<types>
    <type name='Person'
        targetNamespace='http://tempuri.org/type'
        uses='PersonMapperObject'/>
</types>

Now we have three of the four functions callable through our Web Service. We have one more left to go—GetAuthorNames.

The test case for this deployment is this: ASP.NET must be able to create proxy using the WSDL and it must be able to consume the Web Service without requiring the client application developer to write any code. To serialize an array of Name objects, the Web Service had to handle serialization of the objects on its own. For this reason, ASP is used to handle SOAP requests for the Web Service.

To handle any requests, the ASP code will read in the message, check to make sure that the caller wants the GetAuthorNames Web Method, and then hand control over to GetAuthorNames. Otherwise, the request will be handled by the SOAP Toolkit.

When the request comes through, the SOAP Toolkit does not have the intelligence to serialize an array of objects. It will not do this even if it knows how to serialize individual items of the same type. Instead, the server has to do this itself. The ASP code, shown in Listing 10.8, handles this by passing the Request and Response objects to the Visual Basic GetAuthorNames function.

Listing 10.8. Giving the Response to the Web Method from Ch10STKExSvr.asp
Option Explicit
On Error Resume Next
Response.ContentType = "text/xml"
Dim SoapServer
Dim aSvc
If Not Application("Ch10STKExSvrInitialized") Then
  Application.Lock
  If Not Application("Ch10STKExSvrInitialized") Then
    Dim WSDLFilePath
    Dim WSMLFilePath
    WSDLFilePath = Server.MapPath("Ch10STKExSvr.wsdl")
    WSMLFilePath = Server.MapPath("Ch10STKExSvr.wsml")
    Set SoapServer = Server.CreateObject("MSSOAP.SoapServer")
    If Err Then SendFault "Cannot create SoapServer object. " _
        & Err.Description
    SoapServer.Init WSDLFilePath, WSMLFilePath
    If Err Then SendFault "SoapServer.Init failed. " & Err.Description
    Set Application("Ch10STKExSvrServer") = SoapServer
    Application("Ch10STKExSvrInitialized") = True
  End If
  Application.UnLock
End If
If ( Request.ServerVariables("HTTP_SOAPACTION") = _
    """http://tempuri.org/action/TheService.GetAuthorNames""" ) Then
    ' Call out to the object ourself
    Set aSvc = Server.CreateObject("Ch10STKEx.TheService")
    aSvc.GetAuthorNames Request, Response
    If Err <> 0 Then
        Response.Write "<Error><num>" & Err.number & "</num>" & _
            "<desc>" & Err.Description & "</desc></Error>"
    End If
    Set aSvc = Nothing
Else
    Set SoapServer = Application("Ch10STKExSvrServer")
    SoapServer.SoapInvoke Request, Response, ""
End If
						

The ASP code should handle any errors and send SOAP Fault back if needed. To see how this is done, look at the Ch10STKExSvr.asp file in the Ch10STKExSvr project or generate an ASP listener using the WSDL Generator. My code is no different.

When this has been done, all you need to do is serialize the list of names to XML. When serializing arrays using SOAP-encoding, ASP.NET does best when arrays get serialized as separate top-level elements within the SOAP body. To do this, the code writes out the complete SOAP message. The SOAP Toolkit refers to this as the low-level SOAP interface. This means the code must write out the Envelope as well as any required XML namespaces, SOAP headers, or body elements. Listing 10.9 shows a complete listing of the GetAuthorNames function. The first part of the function initializes the array, followed by code that writes out the SOAP response.

Listing 10.9. Handling the GetAuthorNames Request
Const TYPE_ATTR As String = "types"
Const TYPE_NS As String = "http://tempuri.org/type"
Const SOAP_ENC_NS As String = _
    "http://schemas.xmlsoap.org/soap/encoding/"
Const SOAP_ENC As String = "SOAP-ENC"
Const XSD_NS As String = "http://www.w3.org/2001/XMLSchema"
Const XSI_NS As String = "http://www.w3.org/2001/XMLSchema-instance"
Const XSD As String = "xsd"
Const XSI As String = "xsi"

Public Sub GetAuthorNames(ByVal Request As ASPTypeLibrary.Request, _
                   ByVal Response As ASPTypeLibrary.Response)

    ' Declare all the types we need
    Dim serializer As New MSSOAPLib.SoapSerializer
    Dim aName As Variant
    Dim retval(1 To 3) As Name
    Dim id As Long
    Dim index As Long
    Dim arrayLBound As Long
    Dim arrayUBound As Long
    Dim arraySize As Long
    Const MethodName As String = "GetAuthorNames"

    For index = 1 To 3
        Set retval(index) = New Name
    Next index

    retval(1).First = "Scott"
    retval(1).Middle = "Christopher"
    retval(1).Last = "Seely"

    retval(2).First = "Eric"
    retval(2).Middle = ""
    retval(2).Last = "Smith"

    retval(3).First = "Deon"
    retval(3).Middle = ""
    retval(3).Last = "Schaffer"

    id = 1

    ' Get the bounds of the array.
    arrayLBound = LBound(retval)
    arrayUBound = UBound(retval)
    arraySize = arrayUBound - arrayLBound + 1

    ' Initialize the serializer and tell it to write
    ' out to the Response object.
    serializer.Init Response

    ' Write out the envelope and the namespaces used by the
    ' response
    serializer.startEnvelope
    serializer.SoapNamespace XSD, XSD_NS
    serializer.SoapNamespace XSI, XSI_NS
    serializer.SoapNamespace TYPE_ATTR, TYPE_NS
    serializer.SoapNamespace SOAP_ENC, SOAP_ENC_NS

    ' Start the Body and setup the reference to the array
    ' of references to the names.
    serializer.startBody
    serializer.startElement "GetAuthorNamesResponse", TYPE_NS, , _
        TYPE_ATTR
    serializer.startElement "GetAuthorNamesResult"
    serializer.SoapAttribute "href", , "#id" & id
    serializer.endElement
    serializer.endElement

    ' This is the array of name references.
    serializer.startElement "Array", SOAP_ENC_NS, , SOAP_ENC
    serializer.SoapAttribute "id", , "id" & id
    serializer.SoapAttribute "arrayType", SOAP_ENC_NS, _
        TYPE_ATTR & ":Name[" & (arraySize) & "]"

    ' Write out the references
    For index = 1 To arraySize
        serializer.startElement "Item"
        serializer.SoapAttribute "href", , "#id" & (id + index)
        serializer.endElement
    Next index
    serializer.endElement


    ' Now, write out the names
    id = id + 1
    For Each aName In retval
        serializer.startElement "Name", TYPE_NS, , TYPE_ATTR
        serializer.SoapAttribute "id", , "id" & id
        serializer.SoapAttribute "type", XSI_NS, _
            TYPE_ATTR & ":Name", XSI
        serializer.startElement "First"
        serializer.SoapAttribute "type", XSI_NS, _
            XSD & ":string", XSI
        serializer.writeString aName.First
        serializer.endElement
        serializer.startElement "Middle"
        serializer.SoapAttribute "type", XSI_NS, _
            XSD & ":string", XSI
        serializer.writeString aName.Middle
        serializer.endElement
        serializer.startElement "Last"
        serializer.SoapAttribute "type", XSI_NS, _
            XSD & ":string", XSI
        serializer.writeString aName.Last
        serializer.endElement
        serializer.endElement
        id = id + 1
    Next aName

    ' Close up the Body and the Envelope.
    serializer.endBody
    serializer.endEnvelope

End Sub
						

Listing 10.10 shows the SOAP message this code creates. Looking at this message, you should be able to see what all the previous methods do. SoapNamespace adds the namespace and identifier to the currently active element. SoapAttribute adds the attribute to the currently active element. Besides being able to correctly create and close a header, body, and envelope, the toolkit also can create arbitrary elements.

Listing 10.10. SOAP Message Created by GetAuthorNames
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:types="http://tempuri.org/type"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <types:GetAuthorNamesResponse>
            <GetAuthorNamesResult href="#id1" />
        </types:GetAuthorNamesResponse>
        <SOAP-ENC:Array id="id1" SOAP-ENC:arrayType="types:Name[3]">
            <Item href="#id2" />
            <Item href="#id3" />
            <Item href="#id4" />
        </SOAP-ENC:Array>
        <types:Name id="id2" xsi:type="types:Name">
            <First xsi:type="xsd:string">Scott</First>
            <Middle xsi:type="xsd:string">Christopher</Middle>
            <Last xsi:type="xsd:string">Seely</Last>
        </types:Name>
        <types:Name id="id3" xsi:type="types:Name">
            <First xsi:type="xsd:string">Eric</First>
            <Middle xsi:type="xsd:string"></Middle>
            <Last xsi:type="xsd:string">Smith</Last>
        </types:Name>
        <types:Name id="id4" xsi:type="types:Name">
            <First xsi:type="xsd:string">Deon</First>
            <Middle xsi:type="xsd:string"></Middle>
            <Last xsi:type="xsd:string">Schaffer</Last>
        </types:Name>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
						

So, did all this work pay off? You bet. Ch10STKExTest contains the Visual Basic .NET console application that consumes the Web Service. The developer did not have to write any custom code to transform the XML into an array of Name. Listing 10.11 shows all the code I had to write to use every method the Web Service exposes.

Listing 10.11. Visual Basic .NET Client
Sub Main()
    Dim svc As New localhost.Ch10STKExSvr()
    Try
        System.Console.WriteLine(svc.HelloWorld())
        Dim rndNums() As Integer = svc.GetRandomNumbers(10, 3, 19)
        Dim aVal As Integer
        For Each aVal In rndNums
            System.Console.Write(aVal.ToString() & " ")
        Next
        System.Console.WriteLine()
        Dim aPerson As localhost.Person
        aPerson = svc.GetPerson()
        System.Console.WriteLine(aPerson.theName.First & " " & _
            aPerson.theName.Middle & " " & aPerson.theName.Last & _
            " " & aPerson.birthDay.ToString())
        System.Console.WriteLine()

        Dim theNames() As localhost.Name = svc.GetAuthorNames()
        Dim aName As localhost.Name
        For Each aName In theNames
            System.Console.WriteLine(aName.First & " " & _
                    aName.Middle & " " & aName.Last)
        Next
    Catch ex As Exception
        System.Console.WriteLine(ex.ToString())
    End Try
    System.Console.ReadLine()
End Sub
						

Now that the Web Service has been written, let's take a look at how to deploy it.

Deploying Your SOAP Web Service

When it comes time to deploy the Web Service, you need to do a few simple things. First, create a virtual directory on the Web Server and map that directory to the location of the WSDL, WSML, and (if you are using ASP) the ASP files. You then need to install the SOAP Toolkit on the server. Finally, install the COM object and register it using regsvr32.exe. The DLL containing the COM object needs to live in a directory that any clients will appear as. If you use some form of authentication, you will need to make sure that those users have access to the directory. Likewise, if the default IIS user will be accessing the file, he or she needs access to the directory in which the COM object resides. Most issues with accessing the Web Service through the SOAP Toolkit involve improperly set permissions.

Other than the previously mentioned items, you do not have to do anything special to deploy a COM object as a Web Service. Now that the server side is covered, let's look at the client side.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.81.33