In: Categories » Computers and technology » AJAX » Using HTTP Validation
The better approach is to use the HTTP validation model. This model sends each response with a ticket that references the uniqueness of the data. If the client wants to download the content again, the client sends the server a ticket from the last download. The server compares the sent ticket with the ticket that it has, and if the server notices the tickets are identical, it sends an HTTP 304 to indicate no changes have occurred. At that point, the client can retrieve the old content from the cache and present it to the user as the latest and greatest. The HTTP validation model still requires an HTTP request, but it does not include the cost of generating and sending the content again. In terms of an HTTP conversation, the HTTP validation model is implemented as follows. This example illustrates a request from a client and the response from the server.
Request 1 GET /ajax/chap04/cachedpage.html HTTP/1.1 Accept: */* Accept-Language: en-ca Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50215) Host: 127.0.0.1:8081 Connection: Keep-Alive Response 1 HTTP/1.1 200 OK ETag: W/"45-1123668584000" Last-Modified: Wed, 10 Aug 2005 10:09:44 GMT Content-Type: text/html Content-Length: 45 Date: Wed, 10 Aug 2005 10:11:54 GMT Server: Apache-Coyote/1.1 <html> <body> Cached content </body> </html>
The client makes a request for the document /ajax/chap04/cachedpage.html. The server responds with the content, but there is no Cache-Control or Expires identifier. This seems to indicate that the returned content is not cached, but that is not true. The server has indicated that it is using the HTTP validation model, and not the HTTP expiration model. The page that is returned has become part of a cache identified by the unique ETag identifier. The ETag identifier, called an entity tag, could be compared to a unique hash code for an HTML page. The letter W that is prefixed to the entity tag identifier means that the page is a weak reference and the HTTP server may not immediately reflect updates to the page on the server side. The next step is to refresh the browser and ask for the same page again. The HTTP conversation is as follows.
Request 2 GET /ajax/chap04/cachedpage.html HTTP/1.1 Accept: */* Accept-Language: en-ca Accept-Encoding: gzip, deflate If-Modified-Since: Wed, 10 Aug 2005 10:09:44 GMT If-None-Match: W/"45-1123668584000" User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50215) Host: 192.168.1.100:8081 Connection: Keep-Alive Response 2 HTTP/1.1 304 Not Modified Date: Wed, 10 Aug 2005 10:11:58 GMT Server: Apache-Coyote/1.1
When the client makes the second request, the additional identifiers If-Modified-Since and If-None-Match are sent in the request. Notice how the If-None-Match identifier references the identifier of the previously sent ETag value. The server queries the URL and generates an entity tag. If the entity tag is identical to the value being sent, the server returns an HTTP 304 code to indicate that the content has not changed. When using entity tags, the client can send an If-Match or an If-None-Match. If the client sends an If-Match, and the data on the server is out of date, the server returns a cache miss error, and not the new data. If the client sends an If-None-Match identifier when the server data is unchanged, the server sends an HTTP 304 return code. If the data is out of date, new data is sent.
The advantage of using the HTTP validation model of caching is that you are always guaranteed to get the latest version at the time of the request. The clients can make the request every couple of seconds, hours, weeks, or whatever period they choose. It is up to the client to decide when to get a fresh copy of the data. Granted, there is still some HTTP traffic due to the requests, but it has been reduced to aminimum. Having said all that, there are situations when using the HTTP expiration model does make sense for example, when the HTML content is static and changes rarely. The HTTP expiration model could be used in the single request model to prune entries from the cache controller when they become stale.
Implementing a Client Cache Using HTTP Validation The single request client cache can be extended to use HTTP validation. What changes in the HTTP validation model is that a physical request will always be made. From the perspective of the metadata navigation, this might cause a small delay due to the necessity to make a request to verify that the data is current. However, the request response cycle is fairly quick, as most cases result in a “not changed” response. The complete implementation of the two required functions for Asynchronous is as follows.
Source: /client/scripts/jaxson/common.js
var HttpValidationCacheController = {
_cache : new Array(),
didNotFindETagError : function( url) { }
}
function HTTPValProcessAndBreakBeforeRequest(xmlhttp, request, settings) {
if (request.action == "GET") {
var obj = HttpValidationCacheController._cache[url];
if (obj != null) {
this.xmlhttp.setRequestHeader( "If-None-Match", obj.ETag);
this.xmlhttp.setRequestHeader( "Pragma", "no-cache");
this.xmlhttp.setRequestHeader( "Cache-Control", "no-cache");
}
}
return false;
}
function HTTPValProcessAndBreakAfterRequest(xmlhttp, request, settings) {
if (xmlhttp.status == 200 && request.action == "GET") {
if (xmlhttp.status == 200) {
try {
var foundetag = xmlhttp.getResponseHeader("ETag");
if (foundetag != null) {
HttpValidationCacheController._cache[url] = {
ETag : foundetag,
Status : xmlhttp.status,
StatusText : xmlhttp.statusText,
ResponseText : xmlhttp.responseText,
ResponseXML : xmlhttp.responseXML
};
}
else {
HttpValidationCacheController.didNotFindETagError(url);
}
}
catch( exception) {
HttpValidationCacheController.didNotFindETagError(url);
}
}
else if (status == 304) {
var obj = HttpValidationCacheController._cache[url];
if (obj != null) {
var fakeXMLHttp = {
status : 200,
statusText : obj.StatusText,
responseText : obj.ResponseText,
responseXML : obj.ResponseXML,
}
try {
settings.onComplete(fakeXMLHttp);
return true;
}
catch (e) {
globals.errorHandler(e);
}
}
else {
throw new Error("Server indicated that this data is in
the cache ");
}
}
}
return false;
}
Take a moment to examine the code. You should see many similarities to the single request client cache code. The major differences have been highlighted in bold. A big difference in the HTTPValProcessAndBreakBeforeRequest request is the addition of the HTTP validation headers identifying the ETag for a previously executed request. Because using HTTP validation means executing a request, HTTPValProcessAndBreakBeforeRequest has to return false in all situations.
The HTTPValProcessAndBreakAfterRequest function is more complicated because its logic is more sophisticated. If HTTP validation is used and the server supports HTTP validation, then the server can respond with two HTTP status codes for every request: 200 and 304. If the status code is 200, either the data has changed or the data has never been changed.
Regardless of the reason for the status code of 200, the data needs to be added to the HttpValidationCacheController cache instance. The one big difference with respect to the single request client cache is the additional storing of the ETag. If the status code is 304, then a fake XMLHttpRequest instance is created and the settings.onComplete method is called, as shown in the single request client cache implementation. Defining and Implementing the REST URLs The architecture and implementation of the client from a general “what to do” perspective is complete. We still need to cover the details of the URLs and the architecture of the server. At this point, I want to show to refresh your memory. The separation between sending and receiving data is very important because it is what makes it possible for a client to make a request today and ask for the answer tomorrow. If the question and answer had to be in the context of a single request, then the client might have to wait for a very long time.
To put this in another context, you can think of the separation of request and response as the difference between standing in line for assistance at a store and selecting a number for assistance. Imagine shopping and buying some specially sized pieces of lumber. Each lumber store has its own strategy of serving its customers. One strategy is to have the customers line up (queue) and wait their turn. But what if the line is long? Will you be tempted to get in line? What if you decide to get in line and realize that you have to go to the washroom, or you need to put some money into the parking meter? The moment you step out of the line, you lose your position and have to start the waiting game from the start. From an architectural perspective queues are annoying for the server because the server needs to manage the request and keep the attention of the client connection.
Another approach, and one that is fairer, is that each customer who wants a special piece of lumber selects a number and fills in an order. The order is submitted to a salesperson, and he or she will tell you roughly when the lumber will be available. During the wait time, you can go to the washroom, have lunch, or shop for other things. The order and token approach makes it possible to decouple the waiting customer from the store to optimize the time it takes to prepare the special pieces of lumber. For example, based on a set of orders, the store can queue the orders based on priority, ability, and complexity. This architecture is unique because you use an HTTP POST to submit the order, and in return you receive an indication of where the code can find the answer. You don’t have to provide the indication, as that might be programmatically determined. In the case of the ticker application, the location is programmatically determined.
/pyservices/trader/historical/tickers
This represents a root URL that accepts HTTP POST and GET. Executing a GET will return a list of links to all available results. The results might be results in the pure calculation sense or, in the case of the ticker application, a listing of tickers being watched. Getting a listing of all available results is useless when you don’t know what the listing means. The ticker application with its listing is easy to decipher because each result represents a watched ticker. But what if the root URL represents a calculation? The potential listing of results URLs might include random numbers used to index the individual results. This illustrates the fact that it is extremely important to be able to distinguish results based on some metadata. If you cannot do so, you are going to run into major problems.
Sometimes, however, you do not want any metadata descriptors. If the POST executes a calculation where the result is meaningful only in a specific context, then it is the responsibility of the client to keep track of the links. Relating this concept to a shopping cart, most stores have them, and there is no metadata descriptor. When you’re in a store, you know which cart belongs to you based on the items in the cart. There is no identifier for the cart, and in the words of a great British comedian, “Until you buy the contents in your shopping cart, it is a great way of rearranging the food in a store.” I want to point out that yes, you can generate results without metadata, but those situations are specialized (and will be covered in Article 7). When executing a POST to the root URL, you are starting a task on the server side. Starting tasks on an HTTP server can be challenging because HTTP servers expect all processing to be completed when the request ends. However, because of the nature of the data, this is not possible; therefore, you need to extend your server.
Extending your server can be as simple as executing a thread to run the task, or it can mean making an interprocess call to start off a process to run the task. Regardless of how you will be running the task, the important aspect is to not run the task in the context of the request. The role of the POST is to combine the data for a task, and then execute a task. After the request has been submitted, the server should return an identifier that represents the task. The identifier could be the metadata, some random number, or some arbitrary link. The returned information should provide enough data for the client to algorithmically create a complete link to the data and metadata.
If the POST does not provide any order information, then the assumption is that the client knows how to compose the metadata and link from the submitted data. The downside to this strategy is that if the server-side metadata changes, the client-side code will need to be updated.
/pyservices/trader/historical/tickers/DELL
This URL represents the root URL of a result. Only the HTTP verbs GET and DELETE are supported. The verb POST may be supported if you wish to recalculate the result of a request. The concern with performing a recalculation is that in a multiuser situation, other users will see the new data and not be aware that some parts of the data have changed. Generally speaking, it is better to consider a result as immutable. The GET verb is supported to allow retrieval of the results. The DELETE verb is supported to prune a result from the list. A pruning is necessary so that the cache does not become cluttered with old results that have no meaning. In the case of the ticker application, DELETE would not be appropriate because ticker history is a time series, and you can turn back the clock. Pruning a time series is only appropriate when you have some data that is considered old and not appropriate for the context.
/pyservices/trader/historical/tickers/DELL/2006
This URL represents a specific part of a result and, if appropriate, should always be supported. Supporting child URLs in a result allows the client code to fine-tune the result.
/pyservices/trader/historical/tickers/DELL?filter=now
This URL represents another way of fine-tuning the results. For example, if a result generates too much data, using the filter with a value of now could fine-tune the result to the last 15. Fine-tuning does run the risk of missing results. For example, say you are waiting for all of the results from a calculation, and therefore periodically call to retrieve the last 15 results. What if the check period is an hour, and during one hour, 30 results are generated? Using a filter to retrieve the last 15 means you will have missed 15 results. You should not use a task-based approach when retrieving the last 15 deletes1 to maintain stability and the ability to reread results that might be lost if a browser stopped downloading the data. You want a way to execute a backfill where the only reliable cache or storage mechanism is the server.
The best way to fine-tune the returned data is to use a cursor on the result set. For example, if there are 15 results to a calculation, in addition to the mentioned URLs each result should be referenced using a unique URL identifying the result number. Think of the result number as being part of a time series. In a time series, you can retrieve all ticks in an hour, but also all ticks in a minute and all ticks in a second.
legal notice
Our website is not responsible for the information contained by this article. Web-articles is a free articles resource.
Suggestion: If you need fresh, daily updated content for your website, feel free to use our service. Click here for more information.
Useful tools and features
related articles
Given the recent interest in Ajax, you’d be forgiven for thinking it was a new technology. In fact, the XMLHttpRequest object has been around for years. In technical terms, asynchronous JavaScript interaction with the server is nothing new. All of the other elements of the Ajax model have also been around for quite some time: CSS, (X)HTML, and DOM Scripting. Yet in 2005, interest in this methodology soared. Could it really be that simply giving this approach a snappy name like Ajax was responsible for the sudde...
2. Understanding the Definition and Philosophy of Ajax
The focus of this article is to provide solutions to some common, general problems and questions that are bound to arise before or during development of Asynchronous JavaScript and XML (Ajax) and Representational State Transfer (REST) applications. These common questions are not always technical in nature, often leaning more toward theory or philosophy of development. The problem with these kinds of questions is that once you begin to think about them, you keep going in a circle and end up where you star...
3. Understanding the Definition and Philosophy of Web Services and SOA
Understanding the Definition and Philosophy of Web Services and SOA Wikipedia offers the following definition of Web services:4 The W3C defines aWeb service as a software system designed to support interoperable machine-to-machine interaction over a network. This definition encompasses many different systems, but in common usage the term refers to those services that use SOAPformatted XML envelopes and have their interfaces described by WSDL. For ex...
4. Understanding the Definition and Philosophy of REST
Understanding the Definition and Philosophy of REST REST is a controversial topic among Web service enthusiasts, because it’s considered to stand for the opposite of what Web services and SOA are trying to achieve. The problem with this thinking is that REST is not in contradiction with the abstract definition of SOA and Web services. REST is in contradiction with technologies such as SOAP, WSDL, and WS-* specifications. The following offers a quick definition of REST:...
5. The Easiest Way to Get Started with Ajax and REST
The Easiest Way to Get Started with Ajax and REST Problem You want to know the best way to get started with writing Ajax and REST. Solution When developing an Ajax and REST application, you must decide on the tools and frameworks you’ll use. The choice is simple: Use whatever you’re using today, and write some Ajax applications. You don’t need to change the tools you’re using today. Whether you’re using ASP.NET, JavaServer Pages (JSP), PHP, Ruby, or Python, you...
6. Testing a Dynamic Contract with Ajax
Coding the Contract Using Test-Driven Development Techniques Coding the contract using agile and test-driven development techniques requires writing a number of tests and implementing aMock URL layer. Problem You want to code the contract using these development techniques. Solution To demonstrate, let’s define a use case, implement the use case as a contract, write a test case(s) to implement the contract, implement the contract in the Mock URL, and finally...
7. Testing the Client Side Logic
Problem You want to effectively test your application’s client-side logic. Theory Testing GUI code tends not to be a productive task because of the complications that arise. The main complication is how to test the correctness of a user interface. Imagine a situation where clicking a button causes a table to be filled with data. Now imagine that when a check box is checked and the button is clicked again, a different table is filled with content. The fact that clicking the same button results in two ...
8. Understanding JavaScript and Types
Understanding JavaScript and Types Problem You want to work around the fact that JavaScript does not have types declared for its variables. Theory JavaScript code does not have any variables with a declared type. The lack of typed variables is apparent when you declare functions. That said, not having typed variable declarations does not mean JavaScript has no types or no type safety. Let’s start out with the simple declaration of a function, as illustrated by the following ex...
9. Coding Using Conventions and Not Configurations
Coding Using Conventions and Not Configurations Problem You want to make your JavaScript constructs more efficient by applying the Rails “convention over configuration” principle to them. Theory You may already be familiar with the programming platform Ruby on Rails, which is used to build Web applications. The focus of this recipe is not Ruby on Rails, but one aspect of Ruby on Rails namely, convention over configuration (see http://en.wikipedia.org/wiki/ Ruby_on_Rails for m...
10. Advantage of parameterless functions in JavaScript
Using Parameterless Functions Problem You want to take advantage of parameterless functions in JavaScript. Theory JavaScript functions for the most part have parameters. You may think that the previous sentence states the obvious after all, without parameters, what data could be passed to a function? JavaScript has the ability to declare functions that have no parameters, even though the caller of the function has passed parameters to the function. For example, let’s look at...
