Creating HTTP Requests using Microsoft's XML Parser
Thought I would write a quick one about something useful I've been wanting to do for sometime now and finally figured out. It is yet another one of those things that can be quite helpful when applied correctly to solve a problem. Our problem seemed simple enough, as the hard ones often do! We were building an e-commerce solution for a client of ours and got to the portion of the application where we had built the shopping cart, but needed to figure out the shipping rate for the entire order. Our client wanted to use the United States Postal Service's (USPS) API for calculating shipping rate. As we began to read the USPS API we quickly realized that the generated request had to be in XML. Personally, you can fit all of my XML knowledge into a very small container - that's how much XML experience I've had. I do however know a fair amount about HTML and creating HTTP POST requests. As we
read through the USPS API, which is about 92 pages (ouch), we discovered basically all we needed to do was build the HTTP Post request, send it, wait for the response, and parse it. Try searching on Notes.net for XML parser and lotusscript and you get a ton of articles mentioning how "nice it would be" to have such functionality. Luckily we found an article by Paul Ray that we modified to solve our problem.
Prerequisites:
This script must be running on the Win32 platform to work, so if you're still reading this - you're in luck. This example describes creating a new HTTP Post request using calls to the MSXML parser that is installed with Internet Explorer 5. We are running Lotus Domino 5.08 on a Windows 2000 Advanced Server platform with Internet Explorer 5 installed and the script worked like a charm.
The Basics:
In our situation, we needed to calculate the shipping rate before we opened the document in "checkout" mode. The Lotusscript agent is called in the WebQueryOpen event to give us the ability to modify field values before the document is opened. Here is the basic structure of the agent:
- Get a handle on the current web document through the session.documentContext
- Strip off some values that were appended to the ?OpenAgent URL
- Initialize the MSXML Object
- Build the HTTP POST Request in Lotusscript string format
- Send the HTTP POST Request
- Receive the response
- Parse out the
XML Tag value - Add that parsed value to a field on the current web document
- Save the current web document
- Null out the MSXML Object
- Open the web document
The Parsing:
The parsing of the XML response is simplified using the above method because the response is now basically just a very large text string. All of you Lotusscript gurus no doubt have tons of scripts to manipulate string data. We used two very basic functions that allowed us to grab the value that was between the <Postage> opening XML tag and the </Postage> closing XML tag. There are several ways to manipulate the text once you've received the response - your method for text parsing is as good as any one elses.
Where improvements can be made:
Obviously not every solution is fool proof unless you develop a solution with superb error checking. In our example, very little error checking is used. Once you receive the MSXML response, you could opt to use the objHTTP.status to test the response code (e.g., 200, 401, etc.).
The Code:
Our Lotusscript agent was created so as to be run from the WebQueryOpen event on one of our forms. The agent itself was set to Run Manually from the Agent List and Run Once(@Commands may be used). See Figure 1 below.
Sub Initialize
%REM
This routine will create a new HTTP POST request, send values, and retrieve the response from the server. This is done via the XMLHTTP object of the MSXML parser.
%END REM
Dim s As New NotesSession
Dim doc As NotesDocument
Dim objHttp As Variant
Dim response As String
Dim request As String
Dim beginTag As String
Dim endTag As String
Dim rightHalf As String
Dim leftHalf As String
On Error Goto ErrHandler
Set db = s.CurrentDatabase
Set doc = s.DocumentContext
query_string = doc.getitemvalue("Query_String")(0)
If Instr(query_string, "&ZC=") Then
destZipCode = Strright(query_string,"&ZC=")
' --- instantiate an MSXML XMLHTTP object
set objHttp = CreateObject("Microsoft.XMLHTTP")
' --- open a new POST request and set the Content-type header
url = |Http://production.shippingapis.com/ShippingAPI.dll|
req = |API=Rate&XML=<RateRequest USERID="5555555555"
PASSWORD="777777777777"><Package ID="TestID">
<Service>Priority</Service><ZipOrigination>55555
</ZipOrigination><ZipDestination>|+destZipCode+|
</ZipDestination><Pounds>5</Pounds><Ounces>10
</Ounces><Container>None</Container><Size>REGULAR
</Size><Machinable></Machinable></Package>
</RateRequest>|
objHttp.open "POST", url, False, "", ""
objHttp.setRequestHeader "Content-type", "application/x-www-form-urlencoded"
objHttp.send(req)
response = objHttp.responseText
beginTag = "<Postage>"
endTag = "</Postage>"
rightHalf = RightBack(response,beginTag)
LeftHalf = myLeft(rightHalf,endTag)
doc.Shipping = Ccur(LeftHalf)
Call doc.Save(True, True)
Cleanup:
Set objHttp=Nothing
newHREF = "/" + db.FilePath + "/0/" + doc.UniversalID + "?OpenDocument"
Print |[| + newHREF + |]|
End If
Exit Sub
ErrHandler:
' --- runtime error occurred
Msgbox Error$, 48, "Runtime Error"
Resume Cleanup
End Sub
The following two functions are those referenced in the above routine and used to extract the required string from within the XML.
Function RightBack ( sourceString As String, searchString As String ) As String
For i% = Len(sourceString) To 1 Step -1
sourceStringBack$=sourceStringBack$ & Mid(sourceString, i%, 1)
Next
For i% = Len(searchString) To 1 Step -1
searchStringBack$=searchStringBack$ & Mid(searchString, i%, 1)
Next
pos% = Instr ( sourceStringBack$, searchStringBack$ )
If pos% > 0 Then pos% = pos% -1
result$ = Left ( sourceStringBack$, pos% )
For i% = Len(result$) To 1 Step -1
turn$=turn$ & Mid(result$, i%, 1)
Next
RightBack=turn$
End Function
Function myLeft ( sourceString As String, searchString As String ) As String
pos% = Instr ( sourceString, searchString )
If pos% > 0 Then pos% = pos% -1
myLeft = Left ( sourceString, pos% )
End Function
In Summary:
Hopefully the method I have described above demonstrates how you too can use the MSXML object and tailor it to your own needs. To do this all you need do is edit the code modifying the URL and the req values and you're off and running! Let your imagination run wild as the possible solutions you can build are endless. You can even use this method to automatically fill out Notes Forms over the web without any user interaction.
About the Author:
With over seven years experience as a Lotus Notes professional, Patrick has worked on some of the largest and most complex groupware implementations. Specializing in infrastructure design, messaging, and application development, Patrick has a broad range of experience designing, developing, deploying, and supporting departmental and enterprise messaging and groupware solutions. A CLP Principal Application Developer and CLP Principal Systems Administrator, Patrick has developed and implemented large scale messaging solutions, business to business (b2b) architecture and development solutions, and production migrations and rollouts. Patrick has coordinated with and traveled to numerous organizations to provide on site business process analysis, groupware consulting, application development, and messaging implementations. Patrick is the CEO and Senior Software Engineer at Ixion, L.L.C. (http://www.ixiononline.com) As an established Lotus Domino developer, Patrick has contributed development articles to and is published in the Domino Update periodical.
Great article
Wonderful article - thanks a lot, we modified the example in order to be able to send data to SAP via an interation tool ( Business connector by web methods ). Previously we tried a java agent to send the data, but this tended to lock the workstation up, this works 100% all the time.
Reply
Re: Great article
Hi guys i tried this hoping to make a call to a url and post data and the thid party url to call mine back and post data ut it hangs up my application evrytime i try this function. Jack
Reply
Thanks for sharing !!!
Thanks, Patrick, for sharing what you learned here. Your article has been very helpful. I was going to use a Java agent to send XML, but ran into a problem because it couldn't send in HTTPS mode.
Your solution with the Microsoft XML Parser works great with HTTPS. Thanks again !!!
Reply
XMLHTTP
can you help me how can i receive request XML in domino if the request is coming from ASP using XMLHTTP
Reply
Operation is disallowed in this session.
Hello,
I am getting the following error when running this script, "Operation is disallowed in this session." The agent runs fine when I run it from my pc, but from the server it gives me this error. The user is not login to the server, it is a public "Anonymous" access form.
Could you please shine some light into my problem.
Thanks.
Ignacio
Reply
Re: Operation is disallowed in this session.
set the agent property to "Allow restricted operations" but be carefuly what the agent does. See these http://www-1.ibm.com/support/docview.wss?uid=swg21106729
Reply
OLE Automation Object
Hi...
I am using a similar code in a scheduled agent . When I run the agent from my client machine it runs perfectly , the moment I schedule it , it gives the following error. I am struggling with this. Please can you help.
Thanks.
Reply
Here is a javascript version of the RightBack fn
I needed a @RightBack function in Javascript.
Thanks Patrick, for providing a LotusScript version that is easier to transcribe into Javascript than the one I have in my library.
Here it is,
function rightBack (strSource, strSearch) { sourceStringBack = ""; searchStringBack = ""; result = ""; turn = ""; pos = -1; for (i = strSource.length; i>-1; i--) { sourceStringBack=sourceStringBack + strSource.charAt(i) } //end for for (i = strSearch.length; i>-1; i--) { searchStringBack=searchStringBack + strSearch.charAt(i) } //end for pos = sourceStringBack.indexOf(searchStringBack); result = sourceStringBack.substring(0,pos) for (i = result.length; i>-1; i--) { turn=turn + result.charAt(i) } return turn }
Reply
help needed
Hello Sir,
Can you please please me how to
send the xml request to the prodution server
in usps on .net
Reply
Fantastic
This was an excellent post, thanks for sharing!
Reply
Hi Patrick,
I think your article is very helpful.
Just wanted to know, can I use the same for sending the xml to an XML server.
Thanks
Saurabh
Reply
Hello Patrick,
Just to let you know, your article saved my day !
FYI I used it to query a Cisco Call Manager from within a Lotus Notes app.
Greatly appreciated,
Philippe
Reply