Sie sind auf Seite 1von 15

Introduction to Ajax

1. XMLHttpRequest
AJAX (Asynchronous Javascript and XML) is like DHTML in that it is a combination of
several existing technologies rather than being a single technology. In this case the
technologies involved are Javascript, a server side language to handle the request, and a
markup language to return the requested data with. The only common part of this
technology is Javascript since different server side languages and markups can be used.
Even with Javascript though we don't have a single standard way to use this technology
because Internet Explorer (which introduced this concept before the standards were
developed) has its own way of handling things.

The very first thing that we need to be able to do in order to retrieve something from the
server to update the current web page (without reloading the whole page) is to create the
object that will perform the processing for us. Modern browsers have a predefined class
called XMLHttpRequest that we can use to define our object directly. Internet Explorer
has provided five different interfaces to provide us with the functionality that we need
each of which is accessed via AxtiveX. As each new version has improved the
functioning of the object we want to grab the most recent version that is installed in the
browser.

There are only a few different ways that we can code the creation of our new AJAX
object. The following version is slightly more efficient than the alternatives that I have
seen.

function createXMLHttp() {
if (typeof XMLHttpRequest != 'undefined')
return new XMLHttpRequest();
else if (window.ActiveXObject)
{
var avers = ["Microsoft.XmlHttp", "MSXML2.XmlHttp",
"MSXML2.XmlHttp.3.0", "MSXML2.XmlHttp.4.0",
"MSXML2.XmlHttp.5.0"];
for (var i = avers.length -1; i >= 0; i--)
{
try {
httpObj = new ActiveXObject(avers[i]);
return httpObj;
}catch(e) {}
}
}
throw new Error('XMLHttp (AJAX) not supported');
}
var ajaxObj = createXMLHttp();

This code starts by checking if the XMLHttpRequest class exists. If it does then we use
that to create our object. If it doesn't then we check if ActiveX is supported (which means
that the browser is running Internet Explorer on Windows with ActiveX enabled). If it is
then we try to create our object using the latest XMLHttp version (version 5.0). If that
fails then we work our way back through the array trying each earlier version in turn until
we find one that is installed in the browser and which therefore allows us to create our
object. If all of those attempts fail or if neither XMLHttpRequest or ActiveX are
supported then we throw an error to indicate that AJAX is not supported in this browser.

2. Open Request

Having created our AJAX object the next thing that we need to do is to initialize (or
open) the object.

To be able to do this we need to know what server side process that we are going to call
that will set return the requested information for us. For the purpose of this example we'll
assume that the server side processing is written in PHP and will be in a file called
myrequest.php but your server side processing can be written in whatever server side
language you prefer. We start by assigning this to a field called url to make it easier to
change later.

We can now open our request to the server by calling the open method that belongs to the
AJAX object that we created in the first tutorial. This method takes three parameters. The
first parameter is the request type ('GET' and 'POST' are the only ones supported by all
browsers). The second parameter is the url of the server side process. The third parameter
defines whether the call will by asynchronous (true) or synchronous (false).

The A in ajax stands for asynchronous and so that is the one we'll choose. Asynchronous
means that the processing will continue on without waiting for the server side retrieval to
complete whereas a synchronous call would stop all other processing and wait for the
response from the server. Using an asynchronous call helps to hide the time that is taken
for the server side retrieval.

var url = 'myrequest.php';


ajaxObj.open("GET", url, true);

This new code gets added after the code that we have already created.

3. Response Event Handler

As we are making an asynchronous request to the server we are not going to wait around
for the server to send back its reply (as we would with a synchronous request). We
therefore need a way to detect when the server has returned the requested information.

Fortunately our XMLHttpRequest object has a property called readyState that contains
one of several values depending on the current status of the request. An associated event
handler called onreadystatechange allows us to attach processing that will be run
whenever the current status in the readyState field is changed.

ajaxObj.onreadystatechange = function() {
ajaxObj.processRequest();}

Again we add this new code to the end of the code that we have already created.

We next need to create our new method (which we have called processRequest) to
actually perform the processing that we want when the readyState changes. The
readyState field actually has five possible values although not all values are actually set
by all browsers. These values are:

• 0 (uninitialized) - we have created our AJAX object but haven't opened it yet.
• 1 (loading) - we have called open but haven't yet sent the request.
This is the status that we would have if we run the code we have so far.
• 2 (loaded) - we have sent the request.
• 3 (interactive) - a partial response has been received.
• 4 (complete) - a complete response has been received and the connection has
been closed.

The only one of these values that we are actually interested in is the last one since this is
the one that tells us that we have received the complete response. We can therefore add
the following code to initially handle the response:

ajaxObj.processRequest = function() {
if (this.readyState == 4) {
// got response
}
}
We'll keep this piece of code separate from the rest of what we have written for the
moment and return to it later.

4. Send the Request

Now that we have an event handler defined to test for when a resonse to our request is
received we are now ready to send our request to the server. We do this by adding one
extra line to our code.

ajaxObj.send(null);

This completes the code that we need to be able to create and send our request. The
complete code that we have at this point looks like this:

function createXMLHttp() {
if (typeof XMLHttpRequest != 'undefined')
return new XMLHttpRequest();
else if (window.ActiveXObject) {
var avers = ["Microsoft.XmlHttp", "MSXML2.XmlHttp",
"MSXML2.XmlHttp.3.0", "MSXML2.XmlHttp.4.0",
"MSXML2.XmlHttp.5.0"];
for (var i = avers.length -1; i >= 0; i--) {
try {
httpObj = new ActiveXObject(avers[i]);
return httpObj;
} catch() {}
}
}
throw new Error('XMLHttp (AJAX) not supported');
}

var ajaxObj = createXMLHttp();


var url = 'myrequest.php';
ajaxObj.open("GET", url, true);
ajaxObj.onreadystatechange = function() {
ajaxObj.processRequest();}
ajaxObj.send(null);

ajaxObj.processRequest = function() {
if (this.readyState == 4) {
// got response
}
}

5. Passing Parameters

Before we move on to looking at how to deal with what we get returned from our AJAX
request and how to set up the server side processing to create that response, let's first look
at how we can tell the server what it is that we want it to do.

In the code that we have looked at so far we have defined myrequest.php as the server
side process that is to handle our request. We actually have three options when it comes
to setting up multiple AJAX requests for different information.

1. We can substitute different server side scripts for myrequest.php and have each
server side script perform different data retrievals for us. This is the obvious
choice where the retrievals are for completely different purposes that have no real
interdependence.
2. As you will remember we called open passing a first parameter of "GET". When
we use get processing we can pass parameters on the end of the url. For example
we can have myrequest.php?type=1 and myrequest.php?type=2 where we pass
different values in the type field that the server side script can access to determine
what we want it to do. We can of course pass multiple arguments this way by
separating them with ampersands.
3. We can change from using "GET" to using "POST" in order to pass a larger
amount of data to the request. To be able to do this we'll need to add a request
header and pass the data in the send command. The following can pass all of the
form fields from the first form on the page.
ajaxObj.open("POST", url, true);
ajaxObj.setRequestHeader("Content-Type"
"application/x-www-form-urlencoded");
ajaxObj.onreadystatechange = function() {
ajaxObj.processRequest();}
var myform = document.forms[0];
var reqBody = getRequestBody(myform);
ajaxObj.send(reqBody);

6. Success or Failure

We already have our method set up ready to handle the information passed back from our
AJAX request and have the if statement in place that confirms that the request is
complete. Of course just because a request is complete doesn't mean that it worked and
returned the needed information. Perhaps the server was down or there is a typo in the
name of the server side script, or we don't have authority to access that script. Just
because the request is complete doesn't mean that it worked. What we need to do now is
to determine whether the request succeeded or failed.

We can determine the status of the completer request by testing the status property of our
request. This field contains the same codes as are returned when a web page is accessed
(I am sure you have come across 404 not found errors before now). The code for a
successful return is 200 so we need to add a test for that into our code before trying to
process what we got back.

ajaxObj.processRequest = function() {
if (this.readyState == 4) {
if (this.status != 200) {
alert('Error : Status '+this.status+' returned.');
} else {
// got response
}
}
}
In some (but not all) browsers there is also a property called statusText that contains a
description of what the error is. Because this is not defined in some browsers, I
recommend that you avoid using this field and build your own error messages based on
the status property.

7. Response Type

If you are the one creating the entire AJAX application then you ought to know exactly
what format you expect to get your response in. We can of course confirm this by
checking the content type defined in the response headers.

Normally the response that we are expecting to get back will be either an XML or a plain
text format so we can test which of these that we have using the following code:

ajaxObj.processRequest = function() {
if (this.readyState == 4) {
if (this.status != 200) {
alert('Error : Status '+this.status+' returned.');
} else {
var cType =
this.getResponseHeader("Content-Type");
if (cType == 'text/xml') {
// XML response
} else if (cType == 'text/plain') {
// plain text response
} else {
alert('unknown content type');
}
}
}
}

8. responseText and responseXML


In the last tutorial we looked at how we can determine whether the response is XML or
plain text. The reason why we must know the answer to this (either by knowing what the
server side script is set up to return or by testing the content type in our code) is that the
response is returned in one of two different fields depending on which of these two
formats it is that is being used.

The following code fragment illustrates these two fields by substituting alerts to display
the appropriate field into the code from our last tutorial where we tested the content type.

if (cType == 'text/xml') {
alert(this.responseXML);
} else if (cType == 'text/plain') {
alert(this.responseText);
} else {
alert('unknown content type');
}

9. Setting Response Type

If the content on the server that you are going to retrieve is static then what is returned is
rather obvious - response.xml contains XML and response.txt contains plain text.
Where you are going to use a server side scripting language to generate the response then
things are a little more complicated.

Whether the server returns XML or plain text depends on how the server side script is
configured. How you configure the server side script depends on which language that it is
that you are using on the server.

One language commonly used for the server side of AJAX is PHP. With PHP the content
type is defined using the header function and the response is returned using echo.

<?
header("Content-Type: text/xml");
// code to generate $response
echo $response;
?>

10. Javascript Object Notation


There are a number of standard formats that have been adopted for AJAX to use to pass
the response back from the server. The one that gives AJAX its name is XML. Another is
JSON (Javascript Object Notation). As JSON is related to Javascript and produces a more
compact response we will use this as our format of choice in the next few tutorials.

In order to use JSON to return the information from the server we will need to do a
couple of things:

• the server script will need to create the response in JSON format and pass it back
as plain text
• The JSON code will need to be converted into regular Javascript in order to be
able to use it to update the current web page.

The easiest way to convert our JSON code into Javascript is to use the json.js utility that
JSON "inventor" Douglas Crockford created for this purpose. With that utility (itself
written in Javascript) attached to our web page we can easily convert the JSON response
into a Javascript object using one line of code:

var respObj = JSON.parse(response);

Once we have the response in a Javascript object we can then use the information from
the object to update the web page (see the DOM tutorials for more details on how to do
that part).

11. Parsing XML

Once you have got your response back from the server you need to break it up into its
component parts (assuming that there is more than one pices to the information) and then
use the DOM to update the web page with the information. Where we get the respose
from the responseText field (which is always available) we then have to parse the content
ourselves in order to determine what parts of the content are what.

When we retrieve XML from the server we have both the responseText and the
responseXML fields being populated and the advantage to using responseXML is that the
work of parsing the XML is done for us.

Presumable since both the Javascript that sends the request and the server side processing
that builds the response based on that request are both on our web site we should know
what XML fields can or might be passed back in the response. We can then use methods
on the responseXML field to retrieve the content of specific fields using near identical
code to that which we would use to retrieve the content of certain containers within our
web page. For example if we are exmecting a number of entries inside <price> </price>
tags in the XML then we can extract them into a Javascript array using:

var xmlDoc = request.responseXML;


var priceArray = xmlDoc.getElementsByTagName('price');

To extract the information from the XML document into Javascript for processing is
effectively identical to how we would retrieve information from the web page for
processing via the DOM except for the names of the containers.

12. Aborting Ajax

Because AJAX is normally processed asynchronously we may have situations where we


want to stop waiting for the responce to come back from the server because it is no longer
relevant. The situation where your visitor leaves the page before the response is received
is handled by the automatic deletion of the request object along with the rest of the page
so the situation where we are most likely to need to abort a prior request is when our
visitor wants to make a new request (for example they have changed their mind about
which option they wanted).

A minor alteration to the code where we create a new ajax object can readily allow our
object to be reused after aborting any outstanding request. Here is the code that we need
to replace:

var ajaxObj = createXMLHttp();

By replacing this statement with an if statement to test if the object already exists and to
abort any exiting request we can reuse an existing object where one already exists and
avoid problems with multiple requests to the server.

if (!ajaxObj)
ajaxObj = createXMLHttp();
else if (ajaxObj.readyState != 0)
ajaxObj.abort();

After running this piece of code if doesn't matter whether a prior request existed as the
state of the AJAX object after running this code is the same regardless of whether the
object previously existed or has been newly created.
13. Multiple Requests

With our requests being asynchronous we are not waiting for one request to return a
response from the server before being able to make another request. There is no reason
why our visitor cannot initiate further requests to the server before the first request has
returned a response. We do have to be a bit careful in how we code this though.

The one thing that we need to avoid is having the same request object used multiple times
simultaneously. In the last tutorial we looked at how to avoid this by aborting the
previous request if one exists prior to generating the new request. If we instead need to be
able to leave that request running and start a new request then we need somewhat
different code. Let's start by modifying our code so that we can handle multiple ajax
requests more easily.

Any help given gratefully recieved.


Dan.

XMLHttp = function() {
self = this;
if (typeof XMLHttpRequest != 'undefined')
return new XMLHttpRequest();
else if (window.ActiveXObject) {
var avers = ["Microsoft.XmlHttp", "MSXML2.XmlHttp", "MSXML2.XmlHttp.3.0",
"MSXML2.XmlHttp.4.0", "MSXML2.XmlHttp.5.0"];
for (var i = avers.length -1; i >= 0; i--) {
try {httpObj = new ActiveXObject(avers[i]);
return httpObj;
} catch(e) {}
}
}
throw new Error('XMLHttp (AJAX) not supported');
}

XMLHttp.prototype.get = function(url) {
self.open('GET', url, true);
self.onreadystatechange = function() {
self.processRequest();}
self.send(null);
}

XMLHttp.prototype.processRequest = function() {
if (self.readyState == 4) {
if (self.status != 200) {
self.resp = 'Error : Status '+this.status+' returned.';
} else {
if (cType == 'text/xml') {
self.resp = 'xml';
} else if (cType == 'text/plain') {
self.resp = 'text';
} else {
self.resp = 'unknown content type';
}
this.response();
}
}
}

ajaxObj = XMLHttp();
ajaxObj.get('myrequest.php');

ajaxObj2 = XMLHttp();
ajaxObj2.get('myrequest.php');

ajaxObj.response = function() {
if (self.resp == 'xml') {
alert(self.responseXML);
} else if (self.resp == 'text') {
alert(self.responseText);
} else {
alert(self.resp);
}
}
ajaxObj2.response = function() {
if (self.resp == 'xml') {
alert(self.responseXML);
} else if (self.resp == 'text') {
alert(self.responseText);
} else {
alert(self.resp);
}
}

Now we can create our request object when it doesn't already exist and arrange to create a
second request object if the first one does exist. What we do with the responses that we
get back can be request dependedt or you may just want to call a common routine passing
it whichever response that comes.
if (!ajaxObj) {
ajaxObj = XMLHttp();
ajaxObj.get('myrequest.php');
} else {
ajaxObj2 = XMLHttp();
ajaxObj2.get('myrequest.php');
}
ajaxObj.response = function() {
if (this.resp == 'xml') {
alert(this.responseXML);
} else if (this.resp == 'text') {
alert(this.responseText);
} else {
alert(this.resp);
}
}
ajaxObj2.response = function() {
if (this.resp == 'xml') {
alert(this.responseXML);
} else if (this.resp == 'text') {
alert(this.responseText);
} else {
alert(this.resp);
}
}

Simply replace the alert statements in the above code with whatever processing you wish
to perform with XML, text, and errors respectively.

14. Caching of Requests

If we have the one page performing multiple requests (either by aborting any outstanding
request or by creating multiple request objects) and those requests are using the GET
method and don't need to send any gata to the server then we have a slight problem with
some browsers.
Browsers try to reduce the amount of data that needs to be retrieved from the server by
caching what has already been retrieved on your computer. A subsequent request for the
same data will grab the cached copy rather than going back to the server to get it.

Unfortunately some browsers will do this with your Ajax request even though the
information on the server may have changed in the meantime any the page doesn't end up
getting refreshed for subsequent requests. (The same thing can happen with full web
pages having changed but that isn't as noticable to most people unless they are the one
editing their own pages and wondering why their browser still shows the old version).

There are two ways that we can fix this - one in the Javascript and on in the server side
code.

The Javascript solution is to update the URL being passed so that we pass a different
value each and every time. We do this by adding a dummy variable to the end of the URL
in the query string and assign it a different value each time that a request is made. The
easiest way to do this is by adding the microsecond value of the current time which will
be different unless your visitor manages to make two requests in the same microsecond.

url = url+'?dummy='+ new Date().getTime();

Doing this means that the URL is different for each request and so the cached copy
doesn't get used. As the server side processing doesn't need the dummy field it just
ignores it and processes exactly the same as if it weren't there.

The better solution though is to update the server side processing itself. What you need to
do is to add a header into the response that instructs the browser not to cache the
information in the first place. How you do this depends on the server side language you
are using. The PHP version of the extra line you need is:

header('Cache-Control: no-cache');

15. Ajax and Security

Since using Ajax implies that we are accessing information from the server we have
exactly the sale security concerns with Ajax as we wpuld have with any normal server
side processing.

Any fields that can be input by a visitor to the page need to be validated preferably both
by the Javascript before sending the request and then again by the server side processing
before responding to the request. This applies in particular to fields where your visitor
can enter what they want but should also be applied (at least on the server end) for
selection lists and radio buttons where the range of responses is limited on the form.

Do not make the mistake of thinking that becaue your Ajax application needs the
Javascript to pass the request to the server in the first place that you can just validate
everything in the Javascript before sending it and let the server side code assume that it
has already been validated. Sure the cross domain restrictions stop someone from using a
copy of your page with a modified version of the Javascript to do the server side call but
there is nothing says that they need to use Ajax at all in attacking the server side
processing from your site. Without appropriate validations done within the server side
code all that anyone needs to do to bypass all of your Javascript security is to set up their
own page that calls the server side processing without using Ajax. The validations that
you build into your Javascript will even tell them where the possible vulnerabilities in the
server processing are to make it even easier for them to bypass your Javascript security.

Without the appropriate tests in your server side code to block injection attacks etc. all of
the data that is available for retrieval by authorised persons via the Ajax call may also be
available in bulk to anyone who knows a few simple tricks in how to modify database
calls from the form fields passed to them.

If you are wondering if your code is vulnerable to a software injection attack, just try
inputting the following into any of your input fields and see what effect it has:

' || 'a'='a

Note that this is only one of the most basic codes used for software injection attacks and
securing from this particular attack doesn't means that there are not other vulnerabilities
in your code.

Das könnte Ihnen auch gefallen