Wrox Home  
Search
Professional Ajax
by Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett
March 2007, Paperback


XMLHttp POST Requests

Now that you've seen how XMLHttp can simplify GET requests, it's time to take a look at POST requests. First, you need to make the same changes to SaveCustomer.php as you did for GetCustomerInfo.php, which means you need to remove extraneous HTML and JavaScript, add the content type information, and output the text:

<?php

    header("Content-Type: text/plain");

    $sName = $_POST["txtName"];
    $sAddress = $_POST["txtAddress"];
    $sCity = $_POST["txtCity"];
    $sState = $_POST["txtState"];
    $sZipCode = $_POST["txtZipCode"];
    $sPhone = $_POST["txtPhone"];
    $sEmail = $_POST["txtEmail"];
    
    $sStatus = "";
        
    $sDBServer = "your.database.server";
    $sDBName = "your_db_name";
    $sDBUsername = "your_db_username";
    $sDBPassword = "your_db_password";

    $sSQL = "Insert into Customers
	 (Name,Address,City,State,Zip,Phone,`E-mail`) ".
     " values ('$sName','$sAddress','$sCity','$sState', '$sZipCode'".
     ", '$sPhone', '$sEmail')";


    $oLink = mysql_connect($sDBServer,$sDBUsername,$sDBPassword);
    @mysql_select_db($sDBName) or $sStatus = "Unable to open database";
        
    if($oResult = mysql_query($sSQL)) {
        $sStatus = "Added customer; customer ID is ".mysql_insert_id();
     } else {
        $sStatus = "An error occurred 
		while inserting; customer not saved.";
    }
    

    mysql_close($oLink);

    echo $sStatus;
?>

This now represents the entirety of SaveCustomer.php. Note that the header() function is called to set the content type, and echo is used to output $sStatus.

In the main page, the simple form that was set up to allow entry of new customer info is the following:

<form method="post" action="SaveCustomer.php" 
      onsubmit="sendRequest(); return false">
    <p>Enter customer information to be saved:</p>
    <p>Customer Name: <input type="text" name="txtName" value="" /><br />
    Address: <input type="text" name="txtAddress" value="" /><br />
    City: <input type="text" name="txtCity" value="" /><br />
    State: <input type="text" name="txtState" value="" /><br />
    Zip Code: <input type="text" name="txtZipCode" value="" /><br />
    Phone: <input type="text" name="txtPhone" value="" /><br />
    E-mail: <input type="text" name="txtEmail" value="" /></p>
    <p><input type="submit" value="Save Customer Info" /></p>
</form>
<div id="divStatus"></div>

You'll note that the onsubmit event handler has now changed to call the function sendRequest() (although the event handler still returns false to prevent actual form submission). This method first assembles the data for the POST request and then creates the XMLHttp object to send it. The data must be sent in the format as a query string:

name1=value1&name2=value2&name3=value3

Both the name and value of each parameter must be URL-encoded in order to avoid data loss during transmission. JavaScript provides a built-in function called encodeURIComponent() that can be used to perform this encoding. To create this string, you'll need to iterate over the form fields, extracting and encoding the name and value. The getRequestBody() function handles this:

function getRequestBody(oForm) {
    var aParams = new Array();
    
    for (var i=0 ; i < oForm.elements.length; i++) {
        var sParam = encodeURIComponent(oForm.elements[i].name);
        sParam += "=";
        sParam += encodeURIComponent(oForm.elements[i].value);
        aParams.push(sParam);
    } 
    
    return aParams.join("&");
}

This function assumes that you will supply a reference to the form as an argument. An array (aParams) is created to store each individual name-value pair. Then, the elements of the form are iterated over, building up a string and storing it in sParam, which is then added to the array. Doing this prevents multiple string concatenation, which can lead to slower code execution in some browsers. The last step is to call join() on the array, passing in the ampersand character. This effectively combines all the name-value pairs with ampersands, creating a single string in the correct format.

String concatenation in most browsers is an expensive process because strings are immutable, meaning that once created, they cannot have their values changed. Thus, concatenating two strings involves first allocating a new string and then copying the contents of the two other strings into it. Repeating this process over and over causes a severe slowdown. For this reason, it's always best to keep string concatenations at a minimum and use the array's join() method to handle longer string concatenation.

The sendRequest() function calls getRequestBody() and sets up the request:

function sendRequest() {
    var oForm = document.forms[0];
    var sBody = getRequestBody(oForm);

    var oXmlHttp = zXmlHttp.createRequest();
    oXmlHttp.open("post", oForm.action, true);
    oXmlHttp.setRequestHeader("Content-Type", 
	"application/x-www-form-urlencoded");
    
    oXmlHttp.onreadystatechange = function () {
        if (oXmlHttp.readyState == 4) {
            if (oXmlHttp.status == 200) {
                saveResult(oXmlHttp.responseText);
            } else {
                saveResult("An error occurred: " + oXmlHttp.statusText);
            }
        }            
    };
    oXmlHttp.send(sBody);        
}

As with previous examples, the first step in this function is to get a reference to the form and store it in a variable (oForm). Then, the request body is generated and stored in sBody. Next comes the creation and setup of the XMLHttp object. Note that the first argument of open() is now post instead of get, and the second is set to oForm.action (once again, so this script can be used on multiple pages). You'll also notice that a request header is being set. When a form is posted from the browser to a server, it sets the content type of the request as application/x-www-form-urlencoded. Most server-side languages look for this encoding in order to parse the incoming POST data properly, so it is very important for it to be set.

The onreadystatechange event handler is very similar to that of the GET example; the only change is the call to saveResult() instead of displayCustomerInfo(). The last line is very important, as the sBody string is passed to send() so that it will become part of the request body. This effectively mimics what the browser does, so all server-side logic should work as expected.

Advantages and Disadvantages of XMLHttp

Undoubtedly, you can see the advantage of using XMLHttp for client-server communication instead of hidden frames. The code you write is much cleaner and the intent of the code is much more apparent than using numerous callback functions with hidden frames. You have access to request and response headers as well as HTTP status codes, enabling you to determine if your request was successful.

The downside is that, unlike hidden frames, there is no browser history record of the calls that were made. The Back and Forward buttons do not tie in to XMLHttp requests, so you have effectively cut off their use. It is for this reason that many Ajax applications use a mixture of XMLHttp and hidden frames to make a truly usable interface.

Another disadvantage, which applies to Internet Explorer only, is that you depend on ActiveX controls being enabled. If the user has your page set up in a particular security zone that doesn't allow ActiveX controls, you cannot access the XMLHttp object. In that case, you may have to default to using hidden frames.

This article is adapted from Professional Ajax by (Wrox, 2006, ISBN: 0-471-77778-1), from chapter 2 "Ajax Basics." Nicholas is also the author of Professional JavaScript for Web Developers and has worked in web development for more than five years. He has helped develop web solutions in use at some of the largest companies in the world.

Page 1 | Page 2 | Page 3 | Page 4