Posting Form Data and sending custom Request Headers
with XMLHTTP
By Peter A. Bromberg, Ph.D.

Peter Bromberg

Recent posts to our Discussion Forums on Eggheadcafe.com lead me to see that there may be many people who are having difficulty understanding how to use XMLHTTP and SERVERXMLHTTP correctly. In particular, people tend to confuse the methods needed to send form data, which is TEXT, vs. sending a complete XML DOMDocument object, which is treated differently.



Since I've already gone through most of the pain involved in learning this tool, I'm taking this opportunity to share some of the things I've learned. First, let me say that XMLHTTP gives the developer a totally unique paradigm in the development of web- based applications, particularly in that we now have the ability to make the client browser far more intelligent and intuitive, to store often - used data there, and to avoid much of the constant form posts and round trips and database calls that previously placed a high load on the webserver.

You can now use a form post NOT to go to the webserver, process your page all over again, and return results to the browser, but instead, you can store frequently used data in XML data-islands in a topmost IFRAME for example, and have global Javascript functions that take data that has changed on the client (in forms for example) and use the onChange events to update your XML data island. When the data is finally ready to be sent to the server, instead of posting the form to the webserver, instead you can now call XMLHTTP to send your XML data island without the unprofessional-looking and server-intensive reloading of the originating page. On top of that, you can return results and refresh the contents of areas of the client page through well -exposed DOM methods, also without reloading the page. This makes for a much more intuitive, "Windows-like" user experience and also helps our application scale better in a heavy load scenario because there is less load on the webserver and the database.

One of the most important things you'll probably need to do in order to get ServerXMLHTTP working properly is to run
Proxycfg -d on the machine that's going to be using it. You can find this tool here: http://msdn.microsoft.com/code/sample.asp?url=/msdn-files/027/001/468/msdncompositedoc.xml

What we are going to illustrate in this short article is:

1. How to send form data (NOT XML) using xmlhttp, receive it on the server side, process it, and send back a result to the originating client. The originating client page will never reload.

2. How to set a custom request header in the originating client page, have this received and processed at the server, and a result also sent back in the result stream.

First, let's design our client page, "SendReqHeader.htm":

<TABLE border='2' align='center'>
<TR><TD colspan="2" align="center">"Last-Cached" Custom <BR>Header Control Demo</TD></TR>
<TR><TD width="150" align='center'>
<LABEL>Name</LABEL>
</TD><TD>
<input name='Customername' type="edit"/>
</TD></TR>
<TR><TD width="150" align='center'>
<LABEL>Telephone number</LABEL>
</TD><TD>
<input type="edit" name="telno"/>
</TD></TR>
</TABLE>
<TABLE align='center'>
<TR><TD width="150" align='center'>
<input type='button' value='send Information' align='center' onclick='sendinfo()'/>
</TD></TR>
</TABLE>
<script language="vbscript">
function sendinfo()
Dim myhttp
if(not(Customername.value = "")) then
datatosend="Name=" & Customername.value & "&TelNo=" &telno.value
Set myhttp=CreateObject("Msxml2.XMLHTTP")
myhttp.open "POST", "http://localhost/XMLREQHEADER/httpreqserver.asp", false
myhttp.setRequestHeader "lastCached", now()
myhttp.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
txtSent.innerText =datatosend
' To the Moon, Alice.....
myhttp.send datatosend
reqstatus.innerText = myhttp.status
txtResult.innerText= myhttp.responseText
set myhttp = Nothing
else
txtSent.innerText= "invalid data"
set myhttp = Nothing
end if
end function
</script>
Status:<div id=reqstatus></div><BR>
Sent:<div id=txtSent></div><BR>
Rec'd:<div id=txtResult></div>

What this page does is get two input values from the user (Customername and TelNo) and create the same type of string from them that a normal form post would contain, in the format : "Name=John&TelNo=4075551239".

Then, it sets a custom header using the setRequestHeader method consisting of the name "lastCached" having a value of now(), which is the VBScript DateTime method. We then must set the Content-Type request header to "application/x-www-form-urlencoded" in order to tell the receiving page that this is a FORM POST, not a regular HTTP GET or any other type of action. Once we do this, the receving ASP page will be able to access the contents of the form collection in the same way most ASP developers are familiar with: Request.Form("Name") and Request.Form("TelNo"). And of course the data is actually POSTED by the XMLHTTP control, NOT by any HTML FORM control. In fact, as I am sure you've noticed, there isn't a FORM control to be found anywhere on this page.

We've also set up a few DIV's to enable us to display what was sent, the XMLHTTP status property, and the results we get back from the XMLHTTP responseText property. Even though we are going to be sending back well-formed XML from the server page, I've used the responseText method in order to be able to display the results. In a production environment, we'd probably be loading the result XML into a DOMDocment object and doing further processing, such as updating an XML data island or some area of the user interface controls.

Now let's look at what's happening on the server in "HttpReqServer.asp":

<%@ language=javascript %>
<%
Response.Expires = -1000;
var result = Server.CreateObject("MSXML2.DOMDocument")
var OutputString="";
var customHeader = Request.ServerVariables("HTTP_lastCached");
OutputString+="<Name>" +Request.Form('Name') +"</Name>"
OutputString+="<TelNo>" +Request.Form('TelNo') +"</TelNo>"
// Get milliseconds between now and when "LastCached" header was created
var timeDiff = new Date(customHeader);
timeDiff = new Date()-timeDiff;
var resTime="<TimeDiff>" +timeDiff + "</TimeDiff>";
OutputString+=resTime;
if(customHeader != "")
{
result.loadXML("<result>" + OutputString +" </result>");
var pi = result.createProcessingInstruction("xml", "version='1.0'");
result.insertBefore( pi, result.firstChild);
result.save(Response);
result.erase;
}
else
{
Response.Write("<P><B>" + OutputString+" </B></P>");
}
%>

Here, we are instantiating a DOMDocument object to handle our result document. We grab the custom Header "lastCached" using the Request.ServerVariables("HTTP_lastCached") ASP intrinsic method (Custom headers have "HTTP_" prepended to them in the servervariables collection).

We begin building a response XML document: OutputString+="<Name>" +Request.Form('Name') +"</Name>", and we set a new Date object using the value of the customHeader we sent from the client. We then create a second date object and do date arithmetic to get the number of milliseconds difference between when the custom Header was created and now. This type of procedure can be used to check the last time a page was loaded (cached) by the client and is extremely useful in making decisions about what and whether to send data back to the client. In this simple example we are simply going to show the time difference and send it back along with the data that was sent, so we load it all into our DOMDocument.

We then use the createProcessingInstruction DOM method to add the standard XML PI to the beginning of our document, and SAVE it directly into the Response object. You can do this because the Response object supports streams and that is what a Saved xml DOM object is. (you can also LOAD a request stream into an XML DOMDocument object) . And out it goes, right back to the XMLHTTP on the client which has been patiently listening for its return document, and we display our stuff with the innerHTML or innerText DHTML DOM element methods.

So there you have it. It's not your father's XML.

 

download the code that accompanies this article

Peter Bromberg is an independent consultant specializing in distributed .NET solutionsa Senior Programmer / Analyst at in Orlando and a co-developer of the NullSkull.com developer website. He can be reached at info@eggheadcafe.com