The XIR client is implemented as an ActiveX control to be included
on a web page with the <OBJECT> tag. When designing this
control, I wanted the ability to queue up several server calls and
send them as part of one HTTP request. This meant there had to be
three methods on the control: One to queue up a server call, another
to send all queued up calls and a third to retrieve results.
Figure 3 XIR Client Interface Design

As you can see from figure 3, the control's interface is
straightforward. CallService accepts a unique service name and all
input parameters(i.e. ByVal parameters) and returns a call sequence
number used later to retrieve call results. Send tells the control to
send an HTTP POST request containing all queued service calls and
retrieve the response. Finally, GetOutput accepts the call sequence
number and returns whatever the service call returned, if
anything.
Due to the simplicity of its interface, the client control is easy
to use. For example, assume you had a server-side component with the
following method: GetOrderInfo(lOrderId As Long) As Recordset. The
code to call this using the XIR control would be as in figure 4.
Figure 4 Sample script code showing how to use the XIR
client
<OBJECT ID=objXIRControl …></OBJECT>
<SCRIPT Language=VBScript>
Sub GetOrderInformation()
Dim callid
Dim orderid
Dim rsOrderInfo
orderid=7
callid=objXIRControl.CallService("GetOrder",orderid)
objXIRControl.Send
Set rsOrderInfo=objXIRControl.GetOutput(callid)
.
.
End Sub
</SCRIPT>
That's all there is to it. The client's CallService method shown
in figure 5 is responsible for converting input parameters to XML and
creating a new servicecall node in the Request XML
document. First, it
creates a new Request document if needed. Then it creates a new
servicecall node using createElement and sets the node's attributes.
It then creates a param node for each input parameter and converts
the parameter's value to XML using Vnt2XML. This handy function
accepts a variant and returns an XML representation of the variant's
contents. Although very useful, the code for this function is fairly
straightforward. How you convert a variant to XML depends of course
on the variant type. For all simple data types (e.g. string, long,
date etc.), it is a simple conversion to string. As I mentioned
earlier, objects can be converted to XML provided they implement
IXIRPersistXML. ADO Recordsets are different because you must use the
ADO Stream object to save the recordset as XML then read that XML out
of the stream as shown in figure 6. This requires ADO 2.5, which, at
the time of this writing, is still in beta. I am using late binding
whenever I reference a Recordset, that way if you do not need to pass
ADO Recordsets around, you can use XIR without ADO 2.5.
Figure 5 Implementation of CallService
Public Function CallService(ByVal sServiceId As String, ParamArray
vntParams()) As Long
Dim oCall As IXMLDOMElement
Dim oParam As IXMLDOMElement
Dim i As Integer
On Error GoTo eh
If moRequestDoc Is Nothing Then
Set moRequestDoc = New DOMDocument
moRequestDoc.loadXML "<XIRRequest></XIRRequest>"
End If
'1. create and append new element for the service call
Set oCall = moRequestDoc.createElement("servicecall")
'set attributes
oCall.setAttribute "serviceid", sServiceId
oCall.setAttribute "callid", CStr(mlNextCallID)
'increment next call id
CallService = mlNextCallID
mlNextCallID = mlNextCallID + 1
'append node to request document
moRequestDoc.documentElement.appendChild oCall
'2. For each parameter passed in, create a new parameter node and
'convert parameter value to XML
For i = LBound(vntParams) To UBound(vntParams)
'Add child node for parameter
Set oParam = moRequestDoc.createElement("param")
'convert to xml
If
Not IsEmpty(vntParams(i)) Then
oParam.Text = Vnt2XML(vntParams(i))
End If
'add the cloned node to the command in the request doc
oCall.appendChild oParam
Next
Exit Function
eh:
CallService = 0
MsgBox "An error occurred in CallService: " & _
vbCrLf & Err.Description, vbExclamation + vbOKOnly, _
"Error in CallService"
End Function
Figure 6 Converting an ADO 2.5 Recordset to an XML string
'convert recordset to XML for passing out
'create stream
Dim oStream As Object
Set oStream = CreateObject("ADODB.Stream")
'set type to text
oStream.Type = 2
'Save RS to stream
vntParamVal.save oStream, 1
'append the new XML to the Param XML
'start from the first child node
Set oTempDoc = New DOMDocument
oTempDoc.loadXML oStream.ReadText
Vnt2XML = oTempDoc.childNodes(0).xml
It is important to note that I am deliberately building the
request document using nodes and attributes instead of just building
one long XML string with simple string concatenation. This allows me
to explicitly set each parameter value as the parameter text without
caring whether this text actually contains XML. If I had used string
concatenation to build the request document, I would have had to
encode each parameter value using entity references such as <
and >. CallService ensures that for each method call the
client makes, the XML request document contains the called service's
unique id and parameter values passed in by the client.
Before moving on to the server side, take a look at Send() in
figure 7. This method uses a couple of interesting techniques to send
the request document using HTTP along with any cookies. First it uses
the XMLHTTP component I discussed earlier to send the HTTP POST
request. It also uses UserControl.Parent to gain access to the
hosting HTML document. This is a powerful, yet not very well known
technique to manipulate an HTML page from within your ActiveX
control. The returned IHTMLDocument2 interface has a Cookie property
that returns a list of all cookies specific to the current page.
These cookies are then all sent to the server in the HTTP request
headers. Finally, the function posts the request and saves the
returned XML response in moResponseDoc for use by GetResults.
Figure 7 Using XMLHTTP component to send XML over HTTP
Public Sub Send()
On Error GoTo eh
Dim oXMLHTTP As MSXML.XMLHTTPRequest
Dim objDoc As IHTMLDocument2
Set oXMLHTTP = New MSXML.XMLHTTPRequest
oXMLHTTP.open "POST", mServerURL, False
'send any document cookies
Set objDoc = UserControl.Parent
If Len(objDoc.cookie) > 0 Then
oXMLHTTP.setRequestHeader "Cookie", objDoc.cookie
End If
'send request
oXMLHTTP.Send moRequestDoc
Set moRequestDoc = Nothing 'reinit
If oXMLHTTP.responseXML.xml = "" Then
MsgBox "No valid reponse returned. " & _
vbCrLf & "Response text is: " & _
oXMLHTTP.responseText, vbCritical, "XIR Client"
Else
'retrieve response
Set moResponseDoc = oXMLHTTP.responseXML
End If
Exit Sub
eh:
MsgBox "Error sending request: " & vbCrLf &
Err.Description
End Sub