Using the SOAP Toolkit on the Client

The SOAP Toolkit allows for clients to use two interfaces to access data. The high-level interface reads in the WSDL file and, based on how you call the methods, just knows what to do. It also allows for low-level interaction where you craft the entire SOAP message and handle the response. You can also create custom type mappers to handle data that you want the high-level interface to be able to handle. As you may have guessed after reading the last section, a custom type mapper uses the low-level interface to make the high-level one easier to work with.

In this section, we will write four different functions that use the high-level interface, a customer type mapper, and the low level interface. All of this will be demonstrated in the VBCh10Test application.

Using the High-Level Interface

The high-level interface allows for you to interact with any Web Service that sends back simple data types or arrays of simple types. Anything else will require you to manipulate the XML by hand somewhere. To use the high-level interface, simply declare an instance of the MSSOAPLib.SoapClient COM object. Then, load up the WSDL file and call the functions as though the SoapClient object supported them. Under the covers, it maps those calls to the WSDL file, makes the calls, and interprets the results for you. Listing 10.12 shows some code that calls HelloWorld and displays the response in a message box.

Listing 10.12. Calling HelloWorld Using the High-Level Interface Using Visual Basic 6.0.
Private Sub HelloWorld_Click()
    Dim soap As New MSSOAPLib.SoapClient
    soap.mssoapinit _
        "C:InetpubwwwrootCh10STKExSvrCh10STKExSvr.WSDL", _
        "Ch10STKExSvr", "TheServiceSoapPort"
    MsgBox soap.HelloWorld

    Set soap = Nothing
End Sub

You can even get good results when you need to read back an array of integers. The GetRandomNumbers call works equally well. This one takes three arguments and returns a set of random numbers within the specified range. To get 10 random numbers between 0 and 10, you would call the Web Service using the code in Listing 10.14.

Listing 10.14. Calling GetRandomNumbers Using the High-Level Interface
Private Sub GetRandomNumbers_Click()
    Dim soap As New MSSOAPLib.SoapClient
    Dim randomNumbers As Variant
    Dim message As String
    Dim i As Long

    soap.mssoapinit _
        "C:InetpubwwwrootCh10STKExSvrCh10STKExSvr.WSDL", _
        "Ch10STKExSvr", "TheServiceSoapPort"

    randomNumbers = soap.GetRandomNumbers(10, 0, 10)

    For i = LBound(randomNumbers) To UBound(randomNumbers)
        message = message & randomNumbers(i) & vbCrLf
    Next i

    MsgBox message
    Set soap = Nothing
End Sub

Listing 10.14 reads a local copy of the WSDL file. The SOAP Toolkit documentation recommends that the WSDL file always be located on the same machine as the client application. You can load the WSDL over the Internet or LAN, but doing so involves longer delays in getting the file. Why is this a safe thing to do? Odds are very good that if the Web Service changes, your client will fail, regardless of how up-to-date the WSDL is. If you get the WSDL from the Web Service computer, the function may work, but the results may be unusable. By storing the WSDL on the client machine, the changes are still breaking ones. As a result, you are better off keeping the WSDL file on the client. A deployed Web Service should not change its interface. If it does, clients are expected to break. This will either force the creators of the clients to update the code or abandon the Web Service for something more stable.

When you need to use simple types that the toolkit understands natively, you do not need to do a lot of work. The toolkit ups the ante when you need to read in a custom type. If you will only read the type using one call, you will not save much time by creating a custom type mapper. On the other hand, if you call the function a lot, a custom type mapper can come in handy. To create one, you need to make your own WSML file and COM object. The WSML file only needs to indicate the type and the COM object that knows how to translate the type into a COM object. The COM library VBCh10TestMappers handles the mapping, and a customized version of Ch10STKExSvr.wsml tells the toolkit how to use the COM library. Listing 10.15 shows the WSML file.

Listing 10.15. Edited WSML File
<?xml version='1.0' encoding='UTF-8' ?>
<servicemapping name='Ch10STKExSvr'>
  <service name='Ch10STKExSvr'>
    <using PROGID='VBCh10TestMappers.PersonMapper'
       cachable='0' ID='PersonMapperObject' />
    <types>
     <type name='Person'
           targetNamespace='http://tempuri.org/type'
           uses='PersonMapperObject'/>
    </types>
  </service>
</servicemapping>

The class mapping the XML to COM objects does not need to keep its dispatch IDs constant. Because the SOAP Toolkit only attaches to the ISoapTypeMapper interface, the toolkit already knows what it is looking for and can handle using the functionality simply through the PROGID. Listing 10.16 shows the three functions that have any code for the client side: Init, varType and read.

Listing 10.16. Client-Side Custom Type Mapper
Dim m_stringMapper As ISoapTypeMapper
Dim m_dateMapper As ISoapTypeMapper

Private Sub ISoapTypeMapper_Init( _
    ByVal pFactory As MSSOAPLib.ISoapTypeMapperFactory, _
    ByVal pSchema As MSXML2.IXMLDOMNode, _
    ByVal xsdType As MSSOAPLib.enXSDType)

   Set m_stringMapper = pFactory.getMapper(enXSDstring, Nothing)
   Set m_dateMapper = pFactory.getMapper(enXSDdate, Nothing)
End Sub

Private Function ISoapTypeMapper_read( _
    ByVal pNode As MSXML2.IXMLDOMNode, _
    ByVal bstrEncoding As String, _
    ByVal encodingMode As MSSOAPLib.enEncodingStyle, _
    ByVal lFlags As Long) As Variant

    Dim aPerson As New Person
    Dim aName As New Name
    Dim nameNode As MSXML2.IXMLDOMNode
    aPerson.Birthday = m_dateMapper.read( _
        pNode.selectSingleNode("birthDay"), _
        bstrEncoding, encodingMode, lFlags)
    Set nameNode = pNode.selectSingleNode("theName")
    aName.First = m_stringMapper.read( _
        nameNode.selectSingleNode("First"), _
        bstrEncoding, encodingMode, lFlags)
    aName.Middle = m_stringMapper.read( _
        nameNode.selectSingleNode("Middle"), _
        bstrEncoding, encodingMode, lFlags)
    aName.Last = m_stringMapper.read( _
        nameNode.selectSingleNode("Last"), _
        bstrEncoding, encodingMode, lFlags)
    aPerson.theName = aName

    Set ISoapTypeMapper_read = aPerson
End Function

Private Function ISoapTypeMapper_varType() As Long
    ISoapTypeMapper_varType = vbObject
End Function
						

To put this all together, the executable tells the mssoapinit method to look in the WSML file for any types it does not know how to handle. When the library sees a Person come over the wire, it will load up the specified type mapper and tell it to read the XML. The method returns a Variant that the client can then use to get at the actual binary data. Using the type mapper allows you to keep your code fairly simplistic. Listing 10.17 shows the client code that takes advantage of the WSML file.

Listing 10.17. Using a Custom Type Mapper to Read a Complex Type
Private Sub GetPerson_Click()
    Dim soap As New MSSOAPLib.SoapClient
    Dim aPerson As Object
    Dim message As String
    Dim i As Long

    soap.mssoapinit _
        "C:InetpubwwwrootCh10STKExSvrCh10STKExSvr.WSDL", _
        "Ch10STKExSvr", "TheServiceSoapPort", _
        "Ch10STKExSvr.wsml"

    Set aPerson = soap.GetPerson
    message = aPerson.theName.First & " " & _
        aPerson.theName.Middle & " " & _
        aPerson.theName.Last & " " & vbCrLf & _
        aPerson.Birthday
    MsgBox message
    Set soap = Nothing

End Sub

So, how do you handle an array of complex data? You execute the function and then manipulate the XML result however you see fit. When the toolkit does not know how to map the data, you will always get back an instance of an IXMLDOMNodeList. Using it, you can see any data returned within the body result. You might think that the serialization we did might make this difficult. It doesn't. Instead, we have three objects that each contain three fields. Listing 10.18 shows how you would extract this information.

Listing 10.18. Calling GetAuthorNames and Reading the IXMLDOMNodeList
Private Sub GetAuthorNames_Click()
    Dim soap As New MSSOAPLib.SoapClient
    Dim theNames As Variant
    Dim content As Variant
    Dim nameData As Variant
    Dim message As String
    Dim i As Long

    soap.mssoapinit _
        "C:InetpubwwwrootCh10STKExSvrCh10STKExSvr.WSDL", _
        "Ch10STKExSvr", "TheServiceCustomHandlerPort"

    theNames = soap.GetAuthorNames
    For Each content In theNames
        For Each nameData In content
            message = message & nameData.Text & " "
        Next nameData
        message = message & vbCrLf
    Next content
    MsgBox message
    Set soap = Nothing

End Sub

So, what would you do if you needed to access the Web Service the hard way? You would use the low-level interface.

Using the Low-Level Interface

The low-level interface allows you to control the creation of the SOAP message. The low-level interface comes in handy when a WSDL file does not exist or when you need to craft the message by hand due to incompatibilities in between toolkits. To create the message, you need to know all the details about what the server is expecting. In particular, you need the following information:

  • The SOAPAction value (only if required for routing)

  • Address to send the request to

  • Name of the message

  • Names of any parameters

  • Order the service requires the parameters to appear in

  • How to interpret any return values

After you have all this information, you can call the Web Service the hard way. Sometimes, you just have to use the low-level interface. We will look at calling HelloWorld by crafting the entire message through custom code.

To create the connection, you need to use two classes—SoapSerializer and SoapConnector. SoapConnector specifies the interface for something that can send and receive SOAP messages. In our case, we will instantiate an HttpConnector to send the request to an HTTP endpoint. We saw SoapSerializer in the server code. It's still responsible for knowing how to create the actual SOAP message. Finally, we will need to use SoapReader. SoapReader knows how to read a SOAP message, and we will use it to read the response. Listing 10.19 shows how to put all these classes together to send and receive a simple SOAP message.

Listing 10.19. Calling HelloWorld Using the Low-Level Interface
Private Sub HelloWorldLowLevel_Click()
    Dim serializer As SoapSerializer
    Dim reader As SoapReader
    Dim connector As SoapConnector

    ' Create the connection to the endpoint
    Set connector = New HttpConnector
    connector.Property("EndPointURL") = _
        "http://localhost/Ch10STKExSvr/Ch10STKExSvr.WSDL"
    connector.Connect

    ' Set the SoapAction (the SOAP Toolkit uses this for
    ' routing messages)
    connector.Property("SoapAction") = _
        "http://tempuri.org/action/TheService.HelloWorld"
    connector.BeginMessage

    ' Create the message serializer
    Set serializer = New SoapSerializer
    serializer.Init connector.InputStream

    ' Write out the SOAP message
    serializer.startEnvelope , _
        "http://schemas.xmlsoap.org/soap/encoding/"
    serializer.startBody
    serializer.startElement "HelloWorld", _
        "http://tempuri.org/message/"
    serializer.endElement
    serializer.endBody
    serializer.endEnvelope

    ' Send the message to the endpoint
    connector.EndMessage

    ' Read the response
    Set reader = New SoapReader
    reader.Load connector.OutputStream
    If Not reader.Fault Is Nothing Then
        ' Display any fault information
        MsgBox reader.faultstring.Text, vbExclamation
    Else
        ' Display the result
        MsgBox reader.RPCResult.Text
    End If
End Sub
						

If the data being returned is more complex, you will have more code that handles the SoapReader.RPCResult member to discover what XML came back. Likewise, a method that takes parameters or complex types will have more serialization code inside the construction of the method name. Whatever you need to do with SOAP, it should be possible using the low-level interface.

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

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