For many Web developers, generating simple requests and receiving simple responses is enough; but for developers who want to master Ajax, a complete understanding of HTTP status codes, ready states, and the XMLHttpRequest object is necessary. In this article, Brett McLaughlin introduces you to the various status codes and shows you how browsers handle them. He also looks at some of the less common HTTP requests used in Ajax.
In the previous article in this series, we took a closer look at the XMLHttpRequest object, which is the centerpiece of an Ajax application and is responsible for handling requests from server-side applications and scripts and processing data returned from server-side components. Since all Ajax applications use the XMLHttpRequest object, you may want to familiarize yourself with this object so that Ajax can perform better.
In this article, I will focus on the three key parts of this request object based on the previous article:
· HTTP ready status · HTTP status code · The request types that can be generated.
These three parts are all in constructing a Factors to consider when requesting; however, too little has been written on these topics. However, if you want to learn more than just the basics of Ajax programming, you'll need to familiarize yourself with the contents of ready states, status codes, and the requests themselves. When something goes wrong with your application - and it always does - if you correctly understand the readiness state, how to generate a HEAD request, or what exactly a 400 status code means, you can debug the problem in 5 minutes instead of 5 hours spent in various frustrations and confusions.
Let's first look at the HTTP ready state.
A closer look at HTTP ready state
You'll remember from the previous article that the XMLHttpRequest object has a property called readyState. This attribute ensures that the server has completed a request, typically using a callback function to read data from the server to update the content of the Web form or page. Listing 1 shows a simple example (it's also an example from the previous article in this series - see Resources).
XMLHttpRequest or XMLHttp: A Name Change
Microsoft™ and Internet Explorer use an object called XMLHttp instead of the XMLHttpRequest object, which is used by Mozilla, Opera, Safari, and most non-Microsoft browsers. For the sake of simplicity, I'll call both objects simply XMLHttpRequest. This is consistent with both what we see on the Web and Microsoft's intention to use XMLHttpRequest as the request object in Internet Explorer 7.0. (See Part 2 for more on this issue.)
Listing 1. Handling the server's response in a callback
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/n/g, "<br />");
} else
alert("status is " + request.status);
}
}
This is obviously the most common (and simplest) use of the ready state. As you can tell from the number "4", there are several other ready states (you also saw this list in the previous article - see Resources):
· 0: The request is not initialized ( open() has not been called yet).
·1: The request has been established, but has not been sent (send() has not been called yet).
·2: The request has been sent and is being processed (usually the content headers can now be obtained from the response).
·3: The request is being processed; usually some data is available in the response, but the server has not yet completed generating the response.
·4: The response is complete; you can obtain and use the server's response.
If you want to understand more than the basics of Ajax programming, you need to know not only these states, but also when they occur and how to use them. First, you need to learn which request states you might encounter in each readiness state. Unfortunately, this is not intuitive and involves several special cases.
Hidden ready state
The first ready state is characterized by the readyState attribute being 0 (readyState == 0), indicating an uninitialized state. This property is set to 1 once open() is called on the request object. Since you usually call open() immediately after initializing a pair of requests, you will rarely see a readyState == 0 state. In addition, the uninitialized ready state has no real use in real applications.
But for our interest, see Listing 2, which shows how to get this ready state when readyState is set to 0.
Listing 2. Getting 0 ready status
function getSalesData() {
//Create a request object
createRequest();
alert("Ready state is: " + request.readyState);
// Setup (initialize) the request
var url = "/boards/servlet/UpdateBoardSales";
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}
In this simple example, getSalesData() is the function that the Web page calls to initiate a request (such as when a button is clicked). Note that you must check the ready status before calling open(). Figure 1 shows the results of running this application.
Figure 1. Ready state 0
Obviously this doesn't buy you much; there are very few situations where you need to make sure that the open() function hasn't been called yet. In the real world of most Ajax programming, the only use of this ready state is to use the same XMLHttpRequest object to generate multiple requests across multiple functions. In this (uncommon) case, you may want to ensure that the request object is in an uninitialized state (readyState == 0) before generating a new request. This essentially ensures that another function is not using the object at the same time.
View the readiness state of the request being processed
. In addition to the 0 ready state, the request object also needs to go through several other ready states of typical requests and responses, and finally ends in the form of ready state 4. This is why you see the line if (request.readyState == 4) in most callback functions; it ensures that the server has finished processing the request and it is now safe to update the web page or update the page based on the request returned from the server. data to perform operations.
It's very simple to see how this state occurs. If the ready status is 4, not only do we have to run the code in the callback function, but we also print the ready status each time the callback function is called. Listing 3 gives an example of implementing this functionality.
When 0 equals 4
, you need to check ready status 0 to ensure that the request object is not in use when multiple JavaScript functions use the same request object. This mechanism can cause problems. Since readyState == 4 represents a completed request, you will often find that request objects in the ready state that are not currently being used are still set to 4 - this is because the data returned from the server has already been used. , but no changes have been made since they were set to the ready state. There is a function abort() that resets the request object, but this function is not really used for this purpose. If you must use multiple functions, it is better to create and use one function for each function rather than sharing the same object between multiple functions.
Listing 3. View readiness
function updatePage() {
// Output the current ready state
alert("updatePage() called with ready state of " + request.readyState);
}
If you are not sure how to run this function, you need to create a function, then call this function in the Web page and have it send a request to the server-side component (such as the function shown in Listing 2, or the function in this series of articles) Examples given in Part 1 and Part 2). Be sure to set the callback function to updatePage() when making the request; to do this, set the request object's onreadystatechange property to updatePage().
This code is an exact demonstration of the meaning of onreadystatechange - every time the ready state of the request changes, updatePage() is called, and then we can see a warning. Figure 2 shows an example of calling this function, where the ready status is 1.
Figure 2. Ready state 1
You can try running this code yourself. Put it into a Web page and activate the event handler (click a button, tab between fields, or use whatever method you set to trigger the request). This callback function will run multiple times - each time the ready state changes - and you can see the warning for each ready state. This is the best way to track the various stages a request goes through.
Browser Inconsistencies
After you have a basic understanding of the process, try accessing your page from a few different browsers. You should notice that browsers are inconsistent in how they handle these ready states. For example, in Firefox 1.5, you would see the following ready states:
·1
·2
·3
·4
This is not surprising since each request status is represented here. However, if you use Safari to access the same application, you should see -- or not -- something interesting. Here's what it looks like in Safari 2.0.1:
·2
·3
·4
Safari actually throws away the first ready state, and there's no obvious reason why; but that's just the way Safari works. This also illustrates an important point: while it's a good idea to ensure that the request's status is 4 before using the data on the server, code written to rely on each transitional ready state will indeed look different on different browsers. result.
For example, when using Opera 8.5, the displayed readiness status is even worse:
·3
·
4Finally, Internet Explorer will display the following status:
·1
·2
·3
·4If
you have problems with your requests, this is the first place to go to identify the problem. The best way is to test this in both Internet Explorer and Firefox - you will see all 4 states and can check what each state of the request is in.
Next, let's take a look at the situation on the response side.
Response data under the microscope
Once we understand the various readiness states that occur during the request process, it's time to look at another aspect of the XMLHttpRequest object - the responseText attribute. Recall what we introduced in the previous article, you can know that this attribute is used to get data from the server. Once the server has finished processing the request, it can place any data needed to respond to the request data into the request's responseText. The callback function can then use this data, as shown in Listing 1 and Listing 4.
Listing 4. Using the response returned on the server
function updatePage() {
if (request.readyState == 4) {
var newTotal = request.responseText;
var totalSoldEl = document.getElementById("total-sold");
var netProfitEl = document.getElementById("net-profit");
replaceText(totalSoldEl, newTotal);
/* Picture out the new net profit */
var boardCostEl = document.getElementById("board-cost");
var boardCost = getText(boardCostEl);
var manCostEl = document.getElementById("man-cost");
var manCost = getText(manCostEl);
var profitPerBoard = boardCost - manCost;
var netProfit = profitPerBoard * newTotal;
/* Update the net profit on the sales form */
netProfit = Math.round(netProfit * 100) / 100;
replaceText(netProfitEl, netProfit);
Listing
1 is fairly simple; Listing 4 is a little more complicated, but they both check the ready state at the beginning and get the value of the responseText property.
Viewing the response text of a request
Similar to the ready state, the value of the responseText property also changes throughout the life of the request. To see this change, use the code shown in Listing 5 to test the response text of the request, as well as their readiness status.
Listing 5. Testing the responseText property
function updatePage() {
// Output the current ready state
alert("updatePage() called with ready state of " + request.readyState +
" and a response text of '" + request.responseText + "'");
}
Now open the web application in your browser and activate your request. To better see the effect of this code, use Firefox or Internet Explorer, as both browsers can report all possible readiness states during the request. For example, in ready state 2, responseText is not defined (see Figure 3); if the JavaScript console is also open, you will see an error.
Figure 3. Response text for ready status 2
However, in ready state 3, the server has put a value in the responseText property, at least in this example (see Figure 4).
Figure 4. Response text for ready status 3
You'll see that the response with a ready status of 3 is different on each script, each server, and even each browser. However, this is still very useful in debugging applications.
Obtaining secure data
All documents and specifications emphasize that data can only be safely used when the readiness status is 4. Believe me, when the ready state is 3, you will rarely find a situation where you cannot get data from the responseText property. However, it's not a good idea to make your own logic in your application dependent on ready state 3 - once you write code that relies on complete data in ready state 3, you are almost responsible for the incomplete data at that time. .
A better approach is to provide some feedback to the user that when in ready state 3, the response will be soon. Although using functions like alert() is obviously a bad idea - using Ajax and then blocking the user with an alert dialog box is obviously wrong - you can update fields in a form or page when the ready state changes. For example, for ready state 1, set the width of the progress indicator to 25%, for ready state 2, set the width of the progress indicator to 50%, and for ready state 3, set the width of the progress indicator to 25%. The width is set to 75%, and when the ready state is 4, the width of the progress indicator is set to 100% (complete).
Of course, as you've already seen, this method is very clever, but it is browser dependent. On Opera you never see the first two ready states, and on Safari there is no first one (1). For this reason, I left this code as an exercise and did not include it in this article.
Now it's time to look at status codes.
A Deeper Look at HTTP Status Codes
With the ready state and the server response you learned in Ajax Programming Techniques, you can add another level of complexity to your Ajax applications -- using HTTP status codes. There's nothing new about Ajax in this code. They've been around since the dawn of the Web. You may have seen several status codes in Web browsers:
· 401: Unauthorized · 403: Forbidden · 404: Not Found
You can find more status codes (see Resources for a complete list). To add an additional layer of control and response (and more robust error handling) mechanisms to your Ajax applications, you need to properly view status codes in requests and responses.
200: Everything is OK
In many Ajax applications, you will see a callback function that is responsible for checking the readiness status and then continuing to utilize the data returned from the server response, as shown in Listing 6.
Listing 6. Callback function that ignores status code
function updatePage() {
if (request.readyState == 4) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/n/g, "<br />");
}
}
This turns out to be a short-sighted and wrong approach to Ajax programming. If the script requires authentication and the request does not provide a valid certificate, the server returns an error code such as 403 or 401. However, because the server responded to the request, the ready status is set to 4 (even though the response was not what the request expected). Ultimately, the user doesn't get valid data, and serious errors can occur when JavaScript attempts to use server data that doesn't exist.
It takes minimal effort to ensure that the server not only completes a request, but also returns an "all good" status code. This code is "200", which is reported through the status attribute of the XMLHttpRequest object. To ensure that the server not only completed a request but also reported an OK status, add another check to your callback function, as shown in Listing 7.
Listing 7. Checking for valid status codes
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/n/g, "<br />");
} else
alert("status is " + request.status);
}
}
By adding these few lines of code, you can confirm if there is a problem and the user will see a helpful error message instead of just seeing a page made up of data taken out of context with no explanation.
Redirects and Reroutes
Before we get into the details about errors, it's worth discussing an issue you don't need to worry about when using Ajax - redirections. Among HTTP status codes, this is the 300 series of status codes, including:
301: Moved Permanently 302: Found (request redirected to another URL/URI)
·305: Using a proxy (the request must use a proxy to access the requested resource)
Ajax programmers may not be too concerned about redirection issues, for two reasons:
·First, Ajax applications are usually designed for Written for a specific server-side script, servlet, or application. Ajax programmers are less clear about components that disappear without you seeing them. So sometimes you know that the resource has moved (because you moved it, or moved it by some means), and then you modify the URL in the request and never encounter this result again.
A more important reason is that Ajax applications and requests are encapsulated in a sandbox. This means that the domain that serves the Web pages that generate Ajax requests must be the domain that responds to those requests. Therefore, the Web page provided by ebay.com cannot make an Ajax-style request to a script running on amazon.com; an Ajax application on ibm.com cannot make a request to servlets running on netbeans.org. .
·The result is that your request cannot be redirected to another server without generating a security error. In these cases, you won't get a status code at all. Usually a JavaScript error is generated in the debug console. Therefore, after giving enough thought to status codes, you can ignore the issue of redirect codes altogether.
The result is that your request cannot be redirected to another server without generating a security error. In these cases, you won't get a status code at all. Usually a JavaScript error is generated in the debug console. Therefore, after giving enough thought to status codes, you can ignore the problem of redirect codes altogether.
Errors
Once you receive status code 200 and realize that you can largely ignore the 300-series status codes, the only set of codes you need to worry about are the 400-series codes, which illustrates the different types of errors. Look back at Listing 7 and notice that when handling errors, only a few common error messages are output to the user. Although this is a step in the right direction, these messages are still not very useful in telling users and programmers working on the application what exactly is going wrong.
First, we'll add support for pages not found. This shouldn't actually happen in most production systems, but it's not uncommon when the location of the test script changes or the programmer enters the wrong URL. If you can report 404 errors naturally, you can provide more help to frustrated users and programmers. For example, if a script on the server is deleted, we can use the code in Listing 7 so that the user sees a non-descriptive error like the one shown in Figure 5.
Edge Cases and Difficult Situations
At this point, some novice programmers may wonder what this is about. Here's a fact you need to know: less than 5% of Ajax requests use ready states such as 2 and 3 and status codes such as 403 (actually, this rate is probably closer to 1% or even less) . These situations are very important and are called edge cases - they only occur in very specific situations where the most exotic problems are encountered. Although these situations are not common, these edge cases account for 80% of the problems most users encounter!
For the typical user, the fact that the application works correctly 100 times is usually forgotten, however One mistake in the application will be clearly remembered by them. If you can handle edge cases (or difficult situations) well, you can provide satisfying rewards for users who return to your site.
Figure 5. Common error handling
The user has no way of telling whether the problem is an authentication issue, a script not found (as is the case here), user error, or something else in the code. Adding some simple code can make this error more specific. Please refer to Listing 8, which is responsible for handling the situation where the script is not found or the authentication error occurs, and specific messages will be given when these errors occur.
Listing 8. Checking for valid status codes
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/n/g, "<br />");
} else if (request.status == 404) {
alert ("Requested URL is not found.");
} else if (request.status == 403) {
alert("Access denied.");
} else
alert("status is " + request.status);
}
}
While this is still fairly simple, it does provide a little more useful information. Figure 6 shows the same error as Figure 5, but this time the error handling code better explains to the user or programmer what exactly happened.
Figure 6. Special error handling
In our own application, we might consider clearing the username and password and adding an error message to the screen in the event of an authentication failure. We can use a similar approach to better handle script not found or other 400-type errors (for example, 405 means that an unacceptable request method such as sending a HEAD request is not allowed, and 407 means that proxy authentication is required). However, no matter which option you choose, you need to start processing the status code returned from the server.
Other Request Types
If you really want to have control over the XMLHttpRequest object, consider implementing this final functionality by adding the HEAD request to the directive. In the previous two articles, we have introduced how to generate GET requests; in an upcoming article, you will learn about using POST requests to send data to the server. However, in the spirit of enhanced error handling and information gathering, you should learn how to generate HEAD requests.
Making the request
Making the HEAD request is actually very simple; you call the open() method with "HEAD" (instead of "GET" or "POST") as the first parameter, as shown in Listing 9.
Listing 9. Using Ajax to generate a HEAD request
function getSalesData() {
createRequest();
var url = "/boards/servlet/UpdateBoardSales";
request.open("HEAD", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}
When you generate a HEAD request like this, the server does not return a real response like it would for a GET or POST request. Instead, the server only returns the header of the resource, which includes when the content in the response was last modified, whether the requested resource exists, and a lot of other useful information. You can use this information to learn about the resource before it is processed and returned by the server.
The simplest thing you can do for this kind of request is to simply output the contents of all response headers. This gives you an idea of what's available via the HEAD request. Listing 10 provides a simple callback function that prints the contents of the response header obtained from the HEAD request.
Listing 10. Output the contents of the response header obtained from the HEAD request
function updatePage() {
if (request.readyState == 4) {
alert(request.getAllResponseHeaders());
}
}
See Figure 7, which shows the response headers returned from a simple Ajax application that makes a HEAD request to the server.
You can use these headers individually (from server type to content type) to provide additional information or functionality in your Ajax application.
Checking the URL
You've seen how to check for 404 errors when the URL does not exist. If this turns out to be a common problem - perhaps a particular script or servlet is missing - then you may want to check the URL before making a full GET or POST request. To implement this functionality, generate a HEAD request and then check for 404 errors in the callback function; Listing 11 shows a simple callback function.
Listing 11. Checking whether a URL exists
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
alert("URL exists");
} else if (request.status == 404) {
alert("URL does not exist.");
} else {
alert("Status is: " + request.status);
}
}
}
Honestly, the value of this code is not that great. The server must respond to the request and construct a response to pad the Content-Length response header, so no processing time is saved. Additionally, this takes as much time as generating the request and using a HEAD request to see if the URL exists, since it's generating the request using GET or POST rather than just handling the error code as shown in Listing 7 . However, sometimes it's useful to know exactly what's currently available; you never know when your creativity will kick in or when you'll need a HEAD request!
Useful HEAD requests
One area where you may find the HEAD request very useful is to see the length of the content or the type of content. This can determine whether a large amount of data needs to be sent back to handle the request, and whether the server is trying to return binary data instead of HTML, text, or XML (all three types of data are easier to process in JavaScript than binary data).
In these cases, you simply use the appropriate header name and pass it to the XMLHttpRequest object's getResponseHeader() method. So to get the length of the response, just call request.getResponseHeader("Content-Length");. To get the content type, use request.getResponseHeader("Content-Type");.
In many applications, generating a HEAD request adds no functionality and may even cause the request to be slower (by forcing a HEAD request to get data about the response, and then using a GET or POST request to actually get the response) . However, in situations where you are unsure about a script or server-side component, using a HEAD request can get some basic data without actually processing the response data or requiring a lot of bandwidth to send the response.
Conclusion
For many Ajax and Web programmers, the material presented in this article may seem too advanced. What is the value of generating a HEAD request? When do you need to explicitly handle redirect status codes in JavaScript? These are good questions; for simple applications, the answer is that the value of these advanced techniques is not very great.
However, the web is no longer a place where you just need to implement simple applications; users have become more advanced, customers expect better stability, more advanced error reporting, and if an application is down 1% of the time, Then the manager might be fired for it.
Therefore, your work cannot be limited to simple applications, but requires a deeper understanding of XMLHttpRequest.
·If you can think about various readiness states—and understand how these readiness states differ between browsers—you can quickly debug your application. You can even develop some creative functionality based on the readiness state and report the requested status back to users and customers.
·If you want to control status codes, you can set up your application to handle script errors, unexpected responses, and edge cases. The result is an application that works correctly all the time, not just when everything is fine.
·Add the ability to generate HEAD requests, check whether a URL exists, and confirm whether a file has been modified, so as to ensure that users can obtain valid pages and that the information users see is the latest. ( Most importantly) surprise them how robust and versatile this app is.
The purpose of this article is not to make your application look fancy, but to help you remove the yellow spotlight and highlight the beauty of the text, or look more like a desktop. Although these are all features of Ajax (which will be covered in the next few articles), they are like a layer of cream on the cake. If you can use Ajax to build a solid foundation so that your application can handle errors and problems well, users will come back to your site and application. In the next article, we will add this intuitive technique that will make your customers tremble with excitement. (Seriously, you don’t want to miss the next article!)