AJAX POST Requests

Chapter 10 20 mins

Learning outcomes:

  1. Limitations of GET
  2. What is the POST method
  3. Posting in AJAX
  4. Encoding types of POST requests

Introduction

In this modern era of jaw-dropping web technologies and business shifting their marketing concerns to the web, we see more than a dozen of websites involving huge amounts of data exchange between the client and the server.

Newer web applications are utilising enormous scripts and an array of network roundtrips to tailor content for their users and provide them with a smooth interface to interact with. Large forms and surveys are being shifted to online portals, with sophisticated architecture and tedious coding.

It is now possible to upload files to servers from webpages, check out from online stores, perform online banking and much much more on this road.

Part of this involves being able to send data from the client to the server easily and safely using the POST method. POST, as we'll discover in this chapter, has a couple of interesting advantages over the traditional GET method due to which it enjoys the position of being an active part of the recent possibililty diagram of the web.

Limitations of GET

As we saw in the previous chapter on AJAX Request Methods, there are a couple of methods to send out an HTTP request. The method determines the way data is sent within the request and tailors the action that happens with it on the server.

GET as we all know, is the most common and most basic of all request methods out there. It asks a server to simply get a given file and throw it back.

GET requests can also be used to send data to a server by appending a query string next to the URL. However there are certain limitations to sending data in this way.

Limited data allowed to be sent

First of all there is a prescribed size limit to query strings beyond which we can't send any further data within them. Each browser has its own implementation of this size; but nonetheless the point is that there is some limit. This means that GET fails to win in the case when we want to send large amounts of data in the request.

Is unsafe

Moving further, all the data in a GET request is part of the query string which is a part of the URL. In other words, all our data is visible in the URL of the webpage.

As you'll appreciate, this is particularly undesirable for sending sensitive data such as passwords or credit card numbers to a server - you'd never want these bits of informations to appear in any URLs, would you? This means that we can't use GET, at any cost, when we ought to send sensitive form data across the network.

Lastly, extending these two limitations about GET, this method isn't convenient at all to send file data in the request. The data will usually cross the length limits of the URL and thus prevent any data interchange between the client and the server.

Owing to these problems with GET, the above use cases are instead solved using the request method POST.

Exploring POST

GET is not solely meant to send data from a client to a server. It's definitely used to do so, but the point is that this isn't its purpose.

Rather GET is used to request a server for a given file. For example, when we visit www.example.com/home.html, a request is made to the server of www.example.com, to fetch home.html.

Here, we aren't sending any data in the GET request, but rather just requesting for it.

The POST method is the complete opposite of this:

The HTTP request method POST is meant to literally post data to a server i.e send data to it.

It is a standard for sending complex and sensitive data to the server, with the additional capability of sending as much data as the need be.

There is no limitation, whatsoever, to sending data in a POST request. It's all upto you!

Exploring the layout of a POST request, we see that it comprised of three things: a request line, followed by a block of headers, (followed by a line), and finally a request body.

Let's dissect a POST request in detail...

Dissecting a POST request

Following is the general form of a POST request:

<request-line>
<headers>

<request-body>

First we have a request line which contains the method of the request, followed by the file path after the host name, followed by its HTTP version of the request.

The we have the headers of the request, each on a newline, giving further information about it such as its Host, User-Agent and so on.

After this comes a blank line and finally the body of the request. Each time you dispatch a POST request, all its data goes here (after the blank line).

The body of a POST request is sometimes also referred to as its payload - just in case you ever encounter this name!

Consider the snippet below showing a POST request made to the file foo.php at the URL www.example.com/foo.php:

POST /foo.php HTTP/1.1
Host: www.example.com
Content-Length: 9
Content-Type: application/x-www-form-urlencoded
User-Agent: ....

msg=Hello

In the first line, POST is set as the request method, and then the file path of the request (the string after the hostname) is specified. HTTP/1.1 simply tells that the request is of HTTP version 1.1 - successor to version 1 and predecessor to version 2.

Then we have some headers following, described below:

  • Host holds the hostname of the request's destination URL. In most cases, and as limited by the cross-origin policy, this is usually the same hostname that is of the webpage making the request. This is used to figure out a fully qualified URL of the requested page - by appending the file path to the hostname.
  • Content-Length specifies the byte-length of the request body i.e size of the data in bytes.
  • Content-Type is the most important of all and specifies the encoding type for the POST request. We'll explore it in detail shortly below.
  • User-Agent holds information about the browser that dispatches the request. It's a long, tedious string of characters; and likewise we've omitted it here to keep things clean and simple.

After all this, we have a blank line and finally the request body containing the data msg=Hello. The way this data is represented in the request differs, depending on the value of Content-Type. We'll see the details to this later in this chapter.

Posting data with AJAX

With a solid understanding of how a POST request looks internally, it's now time to see how to combine it with AJAX.

Let's start by evaluating your thinking skills.

What is the first step we need to take in order to send out a POST request in AJAX?

  • Call send() and supply it data of the request.
  • Set respective headers for a POST request.
  • Call open() and change its first argument to "POST".

First of all we'll need to set open()'s first argument to the value "POST". This determines the method for sending out the AJAX request.

Then to send data in the request body we have to supply a value to the method send().

What this method does is that it simply takes its argument, does the neccessary serialization it needs to do on it, and finally appends it to the body of the request, thus completing a POST fetch.

But before we can do this, we have to specify the encoding type of the data. This is essential to be able to smoothly work with the POST data on the server end.

The way we do this is by specifying the Content-Type header and giving it one of the following three values:

  • text/plain
  • application/x-www-form-urlencoded
  • multipart/form-data

The first value text/plain is the default if we don't specify a value for Content-Type explicitly. It's the least used and least useful of all these three content types, however, regardless of this fact we'll show you the way to deal with data sent using it, back at the server.

The second value, and perhaps the most frequent value for Content-Type, application/x-www-form-urlencoded, simply tells that the request body is url-encoded. The syntax of the request body in this case is similar to the syntax of query strings in GET requests.

The third and last value, multipart/form-data is used to deal with the cases where files have to be sent within the request. It operates quite differently than application/x-www-form-urlencoded, using boundaries to match up different data parameters with their corresponding values.

Let's take a closer look at each value...

text/plain

The encoding type text/plain trivially sends out the request body as is - it is sent as plain and simple text.

If we omit the Content-Type header while sending a POST request using AJAX, text/plain is the default value the header is given.

Sending POST data with this encoding type is least preferred for various reasons - one being that the name-value pairs in the request body aren't parsed by PHP. This makes it quite difficult, inefficient and tedious to handle client-side data at the backend.

Normally PHP provides the $_POST array to fetch the values of different parameters in the request. However for text/plain encoding, this array remains unfilled.

Consider the example below that sends some plain textual data to the server which then outputs it using the input stream, as it is:

var xhr = new XMLHttpRequest();
xhr.open("POST", "post-data.php", true);
xhr.onload = function() {
   if (this.status == 200) {
      alert(this.responseText);
   }
}
xhr.send("msg=Hello World");
post-data.php
<?php
   echo file_get_contents("php://input"); // read from standard input
?>

Live Example

application/x-www-form-urlencoded

The encoding type application/x-www-form-urlencoded hints the server that the request's body is in url-encoded format i.e all name-value pairs are encoded.

When constructing an HTML form with the attribute method set to "POST", the default encoding type for sending the data of the form is application/x-www-form-urlencoded.

The body of a POST request with this encoding type has the following general syntax:

name1=value1&name2=value2

Each pair is separated by an ampersand & sign and within the pair, the name and value is separated using an equals = sign. As you can see, this general format is the same as the one for constructing URL query strings.

In most cases, you'll be more than just well off with encoding your POST data using application/x-www-form-urlencoded.

It is both:

  • Parsed by PHP, unlike text/plain.
  • Slightly more performant than multipart/form-data (the third encoding type as we shall see next).

Consider the following example:

var xhr = new XMLHttpRequest();
xhr.open("POST", "post-data2.php", true);
xhr.onload = function() {
   if (this.status == 200) {
      alert(this.responseText);
   }
}
xhr.send("msg=Hello+World");
post-data2.php
<?php
   echo $_POST["msg"]; // output the parameter value of msg
?>

Here we compose a very simple POST request, set its Content-Type to application/x-www-form-urlencoded, using the setRequestHeader() method, and finally write its response to the document once complete.

Live Example

In PHP, the $_POST variable only works for the encoding types application/x-www-form-urlencoded and multipart/form-data!

You are given an input element and the task to post its value to the script search.php, once it loses focus i.e it becomes blur.

The server script at the backend is configured to read this value from the parameter query and use it to perform a search operation in a database. This means that the data in the request is sent as query=inputValue.

For simplicity, the element's onblur event has already been assigned the following handler function, but without any function body:

function sendRequest() {
   // handle the onblur event
   // write your code here...
}

// imagine the line below has been set
// input.onblur = sendRequest

Complete this function such that it creates a new AJAX POST request and sends it to the server along with supplying the value of the input element.

For this task, you shall use onreadystatechange to track the state changes of the request. Once it's complete and returns with a 200 OK status, you shall pass responseText to alert(). Also take care of any encoding you need to perform in this task!

Since the server needs to read the input value from the parameter query, the best way to send this data is using application/x-www-form-urlencoded (which is parsed by most servers).

With this decided, the next thing we see here is that the value from the input element shall be encoded before being put in the request body. Recall from the AJAX Request Methods chapter, that the function encodeURIComponent() serves this job.

Likewise, following is the complete definition of the handler function:

function sendRequest() {
   var xhr = new XMLHttpRequest();
   xhr.open("POST", "search.php", true);
   xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   xhr.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
         alert(this.responseText);
      }
   }
   xhr.send("query=" + encodeURIComponent(this.value));
}

multipart/form-data

The encoding type multipart/form-data, as we've been saying for so long, is used to handle file data transmission in the request. It works pretty much like application/x-www-form-urlencoded, but with the additional concept of boundaries.

In the Content-Type header of the POST request, first we have the value multipart/form-data and then after it a semi-colon ; and finally a string of the following form: boundary=someRandomValue.

This denotes a boundary for delimiting different segments of the data within the request body.

Consider the following snippet showing an example of a multipart/form-data POST request with a parameter msg of value "Hello":

POST /foo.php HTTP/1.1
Host: www.example.com
Content-Length: 63
Content-Type: multipart/form-data; boundary=XYZ

--XYZ
Content-Disposition: form-data; name="msg"

Hello
--XYZ--

Notice a couple of things over here:

  • The value of Content-Type is a bit different than what we've been seeing so far - first we have the encoding type and then a boundary value, which in this case is equal to XYZ. This boundary value is used inside the request body to be able to separate the data within it, easily.
  • The request body begins with a line containing two hyphens and then the boundary value. This is the syntax of all multipart encoding types i.e each boundary line is prefixed with a set of two hyphen characters.
  • The Content-Disposition header gives us further information about a specific data parameter i.e does it hold data of a file or simply a form input. It even holds the name of a parameter given using the name directive. In this case the header tells us that msg holds simple form data.
  • After this comes a blank line and then finally the value of the parameter.
  • At the end we once again have a boundary line (beginning with -- and followed by XYZ), but this time also suffixed by two hyphens, since it denotes the end of the body.

The request body is comprised of the lines 6 - 10, which means that in order to send this request using AJAX, we'll have to supply this whole value to the send() method as a string.

Following is a simple example, utilising template literals to easily create a string for the request body:

var xhr = new XMLHttpRequest();
xhr.open("POST", "search.php", true);

// set the Content-Type header along with setting a boundary value
xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=XYZ");

xhr.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
      alert(this.responseText);
   }
}

// the request body created using template literals
xhr.send(`--XYZ
Content-Disposition: form-data; name="msg"

Hello
--XYZ--`);

Live Example

We'll shortly be releasing more content on multipart/form-data and handling files in AJAX!