In part one of this article we began to explore the XMLHttpRequest object and how it could be used to interact with live web content. We focused primarily on reading the contents of a web page (a technique known as “screenscraping”) and parsing that data into our script. This time we are going to take a look at the rest of the methods provided by the XMLHttpObject and how we can use them to send data to a web server for remote form submission.
You may want to review part one of this article before going any further if you need to refresh your memory. The XMLHttpRequest object is a versatile tool because it’s installed by Internet Explorer (which means that it’s native to every machine on which you run your script). Its simple construct also makes coding this into your scripts as painless as possible.
We’re going to use the XMLHttpRequest object to submit POST data to a form. In order to do this we’ll need two things. The first is the full URL where we want to send our request. You can find this by looking at the source code for the form. The URL will be in the action attribute of the <form> html tag. Remember that this may be a relative reference so you may have to include the rest of the URL from your browser’s address bar. I have uploaded a sample form to use when testing this script. It is located at the following URL:
Next, we need to construct our POST data string. This is just a string that contains simple data field-value pairs for the form fields that we want to submit. Each data field is separated from the next by an ampersand (&) just as if we were appending it to a URL. The only difference is that we do not need to encode it, so any special characters are okay.
Our example uses two form fields: “name” and “address.” It also contains a hidden field named “action” whose value is set to “data” by default. Let’s assume we want to submit the following data in our form:
Name: John Doe
Address: 123 My Street
With this information, we would construct the following POST data string which I’ve separated across two lines for aesthetic purposes:
Now that we have our data in order, it’s time to send it to the server. We’ll begin by connecting to the XMLHttpRequest ActiveX object with a CreateObject() method call to its namespace. In the last article we used “Microsoft.XMLHTTP” as our namespace. This time we’ll use a newer version that should be installed on every machine with Internet Explorer 5 or higher. If you experience errors, just use the older one.
Set objxmlHTTP = CreateObject(“Msxml2.XMLHTTP”)
Next we open our connection with the XMLHttpRequest object’s Open() method. I’ll provide the syntax again for those who need a refresher.
The first parameter is a text string containing the HTTP method that we wish to use, in our case, “POST.” The method should be in all caps. The second parameter is a string containing the form action URL. The third and final attribute we’ll be using is an optional Boolean value that tells the XMLHttpObject whether or not the data should be sent asynchronously.
The final two parameters are the username and password, respectively, which we would use if our connection prompted for authentication. It should be noted that the username and password values will only be sent if requested by the server.
So continuing on, we open our connection as follows:
objxmlHTTP.Open(“POST”, actionURL, True)
objxmlHTTP.setRequestHeader “Content-Type”, _
“application/x-www-form-urlencoded”
objxmlHTTP.Send(strPostData)
We’ve used the XMLHttpRequest object’s setRequestHeader method to tell the server the MIME type of the data we are posting. This can usually be omitted, but is required by some servers. We then use the Send() method to send our POST data string by setting it as the parameter. Syntax for the Send() method is shown below.
The XMLHttpRequest object has built two built-in properties, ResponseText and ResponseXML, which capture the data returned by the server. The methods return the data as text or as an XML object respectively.
result = objxmlHTTP.ResponseText
Wscript.Echo result
We’ll finish up by echoing back the HTML text that was returned (which happens to be the source for our form submittal page). Our result should look something like this:
That was simple enough, but now what? There has to be more to it. For instance, what if there is a server error while we are trying to connect? How would we know, and better yet, how could we handle that in our script so that our script wouldn’t fail?
In this section, I will be dealing with several more parts of the XMLHttpRequest object.
Properties:
object.ReadyState
object.Status
object.StatusText
Methods:
abort()
getAllResponseHeaders()
getRespondseHeader()
Events:
onReadyStateChange (not used)
In order to catch errors and respond to them, we first need ways of monitoring what’s going on. There are basically two ways of doing this: by monitoring the XMLHttpRequest object’s connection state and by monitoring the server's HTTP response codes. The ReadyState and Status properties do that for us respectively. The ReadyState property returns one of five possible integer values as listed below based on the connection state of the XMLHttpRequest object.
Value
State
Description
0
Uninitialized
Waiting for the Open method.
1
Loading
Waiting for the Send method.
2
Loaded
Send method called. Status and headers available.
3
Interactive
Some data received. You may use ResponseBody and ResponseText to see currently available data.
4
Complete
All data has been received.
The Status and StatusText properties are used to monitor the server responses. Status will return a numerical HTTP code and Status Text will return its text equivalent. So examples might be “404” and “Not Found”, respectively.
With these properties, we can easily monitor our conditions on both the client side and the server side. Our first concern would be a connection error in the form of a timeout. If we can’t make a connection in a reasonable amount of time we should stop the attempt before it throws an error in our script.
By design, the XMLHttpRequest object has an event called onReadyStateChange for this. However, it isn’t fully supported and doesn’t work with VBScript, so we’ll create our own workaround in the form of a timeout loop after we call the Send() method.
varCounter = 0
Do Until (objxmlHTTP.ReadyState = 4) Or (varCounter = 30)
varCounter = varCounter + 1
Wscript.Sleep(1000)
Loop
We start by creating a counter variable. Then we create a loop that increments our counter. The loop will break out any time our ReadyState indicates that the connection is complete or when approximately 30 seconds have passed. We use the Sleep() method to ensure that each iteration takes approximately one second.
Now we’re ready to set up some conditional statements. We need to first determine whether a timeout error has occurred. If so, we should quit trying to make the connection.
If varCounter = 30 Then
objxmlHTTP.Abort()
result = "Your request has timed out. Please try again later."
We use the XMLHttpRequest object’s Abort() method to stop any transfer attempts and then set the result string variable to notify the user.
Elseif objxmlHTTP.Status <> 200 Then
result = objxmlHTTP.Status & ": " & objxmlHTTP.StatusText
Now we perform a quick check to make sure that no server errors occurred. We can do this by checking that the Status property equals 200 (or OK).
Finally, we can process our results.
Else
result = objxmlHTTP.ResponseText
End If
Our Else statement sets our result equal to the text data returned by the XMLHttpRequest object. We’ll finish by closing the connection (by emptying our object) and echoing the results like we did in the last example.
Here are two example outputs after a timeout error and a server error, respectively.
Your request has timed out. Please try again later.
404: Not Found
I’ve included this workaround into the completely reusable function available in the source code for this article. The code also includes an additional function for providing text-based error codes based on the ReadyState property.
Right about now you’re probably asking what else you can do with the XMLHttpRequest object. Well, here are a couple of other uses for you to play around with.
object.getResponseHeader(“header”)
The XMLHttpRequest object is also able to request a website’s headers. It provides two different methods for doing this. The getAllResponseHeaders() method will return all of the headers supplied by the server. The getResponseHeader() method can be used to request a specified header only. The following example demonstrates both.
As you can see, we’ve used the getAllResponseHeaders() method to return all of the headers and the getResponseHeader method to return the Last-Modified headers. An example output might look like this:
Wipe the sweat off of your forehead and pour yourself a cold glass of tea. The hard work is done. You now have two more uses for the XMLHttpRequest object as well as some good methods for error-handling when using it. That’s it for now. I’ll catch you next time. Until then, keep on coding!