AJAX HTTP Statuses

Chapter 4 15 mins

Learning outcomes:

  1. What is an HTTP status
  2. Status codes and messages
  3. The status and statusText properties

Introduction

After understanding readyState and the event onreadystatechange it's now time to bundle up these concepts with the idea of states of a request and see how are all the three used together to process AJAX responses.

In this chapter we will see what are HTTP statuses for AJAX requests, how they indicate what happened with the request at the server end, and finally how to utilise them in JavaScript to understand all this.

What is a status?

Suppose you enter the URL of a webpage in your browser that doesn't exist at all in this entire galaxy - you will get a 404 Not Found page (more than often customised by the website). In simple words, you will encounter an error which in this case is caused due to a non-existent file's request. The server receives the request, searches for the file and when doesn't find one, returns a Not Found message.

This was just a simple instance of what possibly can happen at the server once a request is sent to it. Much more can happen, and in fact does happen, ocassionally at the server that also leads to errors.

For example the server script can take too long to process, or contain syntactical errors, or even deny access to a particular user and so on and so forth. The complete list of all the possible outcomes is so exhaustive and detailed that you'll need to put in a lot of time to understand each one in detail.

The most common and the usually desirable outcome is that of success. Everything can work perfectly on the backend as well, and in this case we get a 200 OK message. As we'll see soon, when developing an AJAX application we have to check for this message particularly to ensure that our request succeeded.

In short, for each request we make, for a given file, we get a response status, or simply status, along with the actual response.

The status of a response determines the result of a request.

The status can help us deduce whether our request succeeded, failed, or redirected and so on. If it wasn't for a status, browsers couldn't understand the condition of a response and then react on it. There would've been no way to figure out what happened with a request at the server end.

Status code and message

Examining the format of the status string, it can be seen that it is made up of two things - a number and a text value.

The number is known as the status code of the request and is a three-digit number, with the first digit indicating the classification of the status. For example, all numbers that begin with a 3, like 301, 302 etc. deal with redirection.

The text value is known as the status message or status text. It gives a short, meaningful explanation of the status code, for example Not Found for the code 404.

As a quick example illustrating both these terms, if a requested page doesn't exist, we get the status string 404 Not Found, where 404 is the status code and Not Found is the status text. Similarly for 200 OK, 200 is the code and OK is the status text. Simple?

Generally when we refer to the status of a request, we are more than often conrened with its status code, and not the whole status string. This means that for a successful fetch we can simply say that "Its status is 200" rather than saying "Its status code is 200" again and again.

Browsers all over the globe use these HTTP statuses to interact with servers and understand what happened internally within them once they received the request. They are programmed to deal with errors i.e for each status returned, they have a set of actions, set up to be taken for ultimately informing the user of the result of the fetch.

AJAX requests operate the same way in that they also receive a status string once complete. Remember, AJAX is just another way of sending requests from a browser; the request itself is the same HTTP request sent normally by browsers. Don't think that AJAX is some new format of sending requests!

However unlike normal requests intiated by the browser, the ones for AJAX aren't directly dealt by the browser once received. Instead this work has to be done by the developer himself. He has to manually write all the code to retrieve the status of the response and perform actions accordingly.

This is where the strength and weakness of AJAX meets. The strength comes from the fact that we have full control over dealing with the response, whereas the weakness comes from the same origin i.e now we have to do everything ourself! A compromise for asynchronous capability.

But how can we determine the status of a request using JavaScript? Let's discuss it.

Retrieving the status

Once the response to a dispatched request is received, its status code goes into the status property while the status text goes into the statusText property of the XMLHttpRequest() object (created earlier for the request itself).

As with the norm, status is of type "number" and statusText is of type "string".

Now before we show up a demonstrating code snippet for both these properties, it's worth testing your understanding of the previous concept on states of a request as linked with statuses.

Make a guess, or a solid judgement, on when does the status property of an AJAX request fill.

For example, given a successful fetch when does status become 200.

  • When readyState becomes 2
  • When readyState becomes 3
  • When readyState becomes 4

For the handler of onreadystatechange, construct a single if statement within which we can successfully start processing the response to our AJAX request.

Hint: It should involve checks for both, the state of the request as well as its status.

What we have to basically do in this task is to come up with an expression that will go inside the if's clause. Let's figure it out.

First we need to check for the state 4, to ensure that the request has completed and secondly check for the status 200 to ensure that the request succeeded at the backend. Once both these are met we can finally proceed further. Following is the code to accomplish this:

xhr.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
      // proceed further
   }
}

Let's start by considering a simple example illustrating the working of statuses.

Following we've set up a code to log messages to the console each time the request's state changes, starting from state 2. Each message contains the state of the request along with its status.

var xhr = new XMLHttpRequest();
xhr.open("GET", "ajax.txt", true);

xhr.onreadystatechange = function() {
   console.log("readyState:", this.readyState, " status:", this.status);
}

xhr.send();
readyState: 2 status: 200
readyState: 3 status: 200
readyState: 4 status: 200

Noticing the console logs above, we see that the status of the request becomes 200 at readyState 2, and not at 4. Why is this? Well it's pure logical reasoning.

The status line precedes the whole set of headers in a response and so is technically known even before all the headers are received. Likewise, status fills up right at the moment once readyState becomes equal to 2; which indicates that the headers of the response have been received.

Talking about the next two logs - they are simply just changes of readyState, that's it - the status remains the same.

Moving on, we already know the fact from the previous chapter that a request completes at state 4, where the actual content is ready to be processed and that this involves a check in the script for readyState == 4.

In addition to this, now we also know that before proceeding further we need to layout a check for status == 200 as well, to confirm a successful fetch. Proceeding without either of these checks is meaningless in an AJAX application, as it can be that the content isn't received or is simply an error response.

Summing it up, following is the complete code to fetch the file ajax.txt and log its content once both the aforementioned conditions are met:

var xhr = new XMLHttpRequest();
xhr.open("GET", "ajax.txt", true);

xhr.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
      // request completed and a successful response
      console.log(this.responseTxt);
   }
}

xhr.send();
The responseText property holds the data of a requested file in textual format. We'll discover it in detail in the next chapter.

Notice the fact here that we've used the && (and) operator to check for both cases in one single expression. Nonetheless you can even write this as two nested if statements - one to check for readyState and the other for status.

xhr.onreadystatechange = function() {
   if (this.readyState == 4) {
      // request completed
      if (this.status == 200) {
         // response successful
         console.log(this.responseTxt);
      }
   }
}

This can particularly be useful when our AJAX application wants to perform certain actions if the status isn't 200. Consider the task below for an example.

Mark is developing an AJAX app for his friend client. Amogst other things in the application, he wants it to log an error once a response is received completely which isn't of the status 200 OK. Following is a segment of his code, tested on a non-existent file.

xhr.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
      // process the response
   }
   else {
      console.error("status:", this.status, this.statusText);
   }
}

He argues that the output is not what he desires i.e errors are logged even before the file is received completely. Unfortunately he is unable to spot where the problem is, and so asks you to help him.

Can you spot the problem and solve it?

The problem lies in the if clause in line 2. It returns false even for the cases when readyState isn't equal to 4 and hence results in executing the else block which then logs an error.

The question clearly says to log errors only once the status isn't 200 and the response has been received completely i.e readyState == 4. To solve this, we simply need to separate the checks for readyState and status. Following is the rectified code:

xhr.onreadystatechange = function() {
   if (this.readyState == 4) {
      if (this.status == 200) {
         // process the response
      }
      else {
         console.error("status:", this.status, this.statusText);
      }
   }
}

In conclusion

Summarising it all, knowing the right way to process an AJAX request is of utmost importance for web developers. They should be able to make a clear distinction between the terms state and status of a request and what purpose each of them serves.

A vivid understanding of such small concepts is essential for them to know, otherwise they'll just be AJAX users, not developers.