This chapter covers
- Identifying the main dialects of Ajax
- Using Ajax with JavaScript and JSON
- Working with XML, XPath, and web services
Ajax is a melting pot, with many different approaches to application design and a
myriad of dialects. In chapter 1 we looked at the mechanics of the XHR object
that lies at the heart of Ajax, and you saw how to wrap those details up in a helper
object. Without the distraction of having to write all that plumbing code by hand,
we can focus on the more interesting issues of structuring the communication
between the server and the client. We can now look at the different categories of
Ajax communication, and teach you how to talk Ajax in a range of dialects.
We’ll continue to work with the Hello World example that we introduced in
chapter 1 and the problem/solution format. We’ll also continue to use frameworks
to handle the low-level Ajax concerns for us and free us up to look at the interesting
issues. Many of the examples will continue to use Prototype’s Ajax. Request
object, but we’ll also take a look at Sarissa and a web services client toolkit. Let’s
begin by looking at JavaScript as a medium of communication.
2.1 GENERATING SERVER-SIDE JAVASCRIPT
When the server returns HTML from an Ajax request, we can generate complex
user interfaces on the fly, but they remain largely static. Any significant interaction
with the application will require further communication with the server. In
many cases, this isn’t a problem, but in others, it is necessary to deliver behavior
as well as content. All client-side behavior is driven by JavaScript, so one way forward
is for the server to generate JavaScript for us.
2.1.1 Evaluating server-generated code
When handling server-generated HTML, we can go a long way using only
innerHTML. When handling server-generated JavaScript, we can make similar use
of the eval() method. JavaScript is an interpreted language, and any snippet of
text is a candidate for evaluating as code. In the next example, we’ll see how to
use eval() as part of the Ajax-processing pipeline.
We’ll stick with our Hello World app through this chapter. In this first
example, we’ll use the response to modify the title element again, as shown in
Figure 2.1.
Problem
The server is returning JavaScript code from an Ajax request. We need to run the
code when we receive it.
Solution
Using eval() is almost as simple as using innerHTML. Listing 2.1 presents the third
incarnation of our Hello World application.
Comparing our earlier solution with listing 2.1, you can see that we’ve had to
change very little. We can still read the response body using the responseText
property of the XHR object, which we then pass straight to eval() .
We want to modify the title block of the page when the response comes in. To
do this, we need to perform a bit of DOM manipulation. The method calls can be
generated directly on the server, as shown in Listing 2.2.
Generally, it is good manners to set the MIME type, but we’ve switched it off here
because Prototype is clever enough to recognize the text/javascript MIME type
and would evaluate it automatically for us. Here we want to do the evaluation
manually in order to demonstrate some general principles, not show off Proto-
type’s power-user features!
Discussion
In this example, we’ve demonstrated the principle of passing JavaScript from the
server to the client, but in the process we’ve raised a few interesting problems.
We’ll fix these up in the next example, but first let’s examine them.
The first problem is that we’ve created a very tight coupling between the client
and server code. The JSP needs to know the id attribute of the DOM element that
it is going to populate. If we change the HTML on the user interface, we need to
alter the server code. In a small example like this one, that’s not too great a burden,
but it will quickly become unscalable.
Second, we’ve created a solution looking for a problem. We aren’t doing anything
here that we couldn’t do more elegantly and simply using innerHTML. As
long as we’re using the response to update a single element on the page, this
approach is overkill.
In the next example, we’re going to address both these points and see how to
reduce the coupling across the tiers as well as update several elements at once.
2.1.2 Utilizing good code-generation practices
When we generate JavaScript on the server, we are practicing code generation.
Code generation is an interesting topic in its own right, with a well-established set
of conventions and ground rules. A cardinal rule of code generation is to always
generate code at the highest level possible.
In the next example, we’re going to increase the complexity of our Hello
World application a little and demonstrate how to tighten up our code generation
to cope with it.
Problem
Writing low-level JavaScript on the server leads to unacceptable tight coupling
between the server and client codebases. This will give our application severe
growing pains and lead to increased brittleness.
At the same time, we want to maintain a list of previous visitors to our page, as
well as display the name of the current visitor. The server is going to classify visitors’
names as either long or short, and we’ll provide a separate list for each (once
again, this is a surrogate for real business logic, because we want to keep the
server code simple in this chapter).
We’ll also pop up an alert message when the data comes in. The revised UI for
the Hello World app is shown in Figure 2.2.
Every time the form is submitted to the server, the most recent name will be
displayed in the title element, as before. We’ll also keep a running list of visitors
in the box elements on the left. Figure 2.3 shows our version 4 Hello World in
action, after several interesting visitors have passed by!
Solution
When the response comes back from the server, we want to update the client with
the new information. The code that the server is sending us is simply a carrier for
some data, so we will simplify the server-generated JavaScript to call a single
updateName() function, passing in the data as arguments.
On the client side, we need to define that updateName() function as handwritten
JavaScript, as shown in Listing 2.3. updateName() will handle all of our expanded
requirements.
In spite of the increased complexity of our requirements, the server-side code has
become simpler. Listing 2.4 shows the JSP used to serve data to version 4 of our app.
The JSP is simpler in terms of length of code, but also in the number of concepts.
It is only concerned with the business logic appropriate to the server and talks to
the client via a high-level API.
Discussion
With this example, we’ve crossed an important threshold and need to update
multiple regions of the UI from a single Ajax call. At this point, the simple
innerHTML approach that we used in example 2 can no longer suffice. In this case,
the requirements were somewhat artificial, but in many real-world applications,
multiple update requirements exist. For example:
- In a shopping cart, adding a new item will result in adding a row to the
cart body, and updating the total price, and possibly shipping costs, estimated
shipping date, and so on.
- Updating a row in a data grid may require updates to totals, paging information,
and so on.
- A two-pane layout in which a summary list is shown on the left and drilldown
details of the selected item on the right will have a tight interdependency
between the two panes.
We solved the multiple-update issue in this case by defining a JavaScript API and
generating calls against that API. In this case, we defined one API method and
called it only once, but a more sophisticated application might offer a handful of
API calls and generate scripts consisting of several lines. As long as we stick to the
principle of talking in conceptual terms, not in the details of DOM element IDs
and methods, that strategy should be able to work for us as the application grows.
An alternative to generating API calls on the server is to generate raw data and
pass it to the client for parsing. This opens up a rich field, which we’ll spend the
remainder of this chapter exploring.
2.2 INTRODUCING JSON
We began our exploration of Ajax techniques in chapter 1 by doing all the processing
on the server and sending prerendered HTML content to the browser. In
section 2.1, we looked at JavaScript as an alternative payload in the HTTP response.
The crucial win here was that we were able to update several parts of the screen at
once. At the same time, we were able to maintain a low degree of coupling between
the client-side and server-side code.
If we follow this progression further, we can divide the responsibilities
between the tiers, such that only business logic is processed server-side and only
application workflow logic on the client. This design resembles a thick-client
architecture, but without the downside of installing and maintaining the client
on client PCs.
In this type of design, the server would send data to the client—potentially
complex structured data. As we noted at the beginning of this chapter, we have a
great deal of freedom as to what form this data can take. There are two strong
contenders at the moment: JavaScript Object Notation (JSON) and XML. We’ll
begin to explore data-centric Ajax in this section with a look at JSON.
A one-minute JSON primer
Before we dive into any examples, let’s quickly introduce JSON. JSON is a lightweight
data-interchange format that can be easily generated and parsed in many
different server-side technologies and in JavaScript. A complete data-interchange
format will provide two-way translation between the interchange format and live
objects, as illustrated in Figure 2.4.
Half of JSON is provided for free as part of the JavaScript language specification,
and the other half is available as a third-party library. That sounds like an
unusual state of affairs, so let’s explain what we mean by it.
First, let’s look at what a JSON definition looks like. The following example
defines a variable customers and stores in it an array attribute called details.
Each array element is a collection of attributes of each customer object. Each customer
object has three attributes: num, name, and city.
var customers = { "details": [
{"num": "1","name":"JBoss","city":"Atlanta"},
{"num": "2","name":"Red Hat","city":"Raleigh"},
{"num": "3","name":"Sun","city":"Santa Clara"},
{"num": "4","name":"Microsoft","city":"Redmond"}
]
} ;
We’ve defined this rather complex variable using JSON syntax. At the same time,
all we’ve written is a standard piece of JavaScript. Curly braces are used to delimit
JavaScript objects (which behave kind of like associative arrays), and square
braces delimit JavaScript Array objects. If you want to brush up on your core Java-Script language skills, we cover these things in greater depth in chapter 4.
Once we’ve defined the variable, we can easily read its values using standard
JavaScript syntax:
alert (customers.details[2].name);
This would display the string "Sun" in an alert box. So far, all we’ve done is take a
standard piece of JavaScript syntax and called it JSON.
We can also create a string variable and then evaluate it using eval() to generate
our variable:
var customerTxt = "{ 'details': [ " +
"{'num': '1','name':'JBoss','city':'Atlanta'}, " +
" {'num': '2','name':'Red Hat','city':'Raleigh'}, " +
" {'num': '3','name':'Sun','city':'Santa Clara'}, " +
" {'num': '4','name':'Microsoft','city':'Redmond'}" +
"] }" ;
var cust = eval ('(' + customerTxt + ')');
alert (cust.details[0].city); //shows 'Atlanta'
There’s no good reason to write code like this when we’re declaring the string
ourselves, but if we’re retrieving the string in a different waysay, as the response
to an Ajax request—then suddenly we have a nifty data format that can express
complex data structures easily and that can be unpacked with extreme ease by the
JavaScript interpreter.
At this point, we have half a data-interchange format. Standard JavaScript
doesn’t provide any way of converting a JavaScript object into a JSON string. However,
third-party libraries can be found at http://www.json.org, which allow us to
serialize client-side objects as JSON, using a function called stringify(). The
JSON library also provides a parse() method that wraps up the use of eval() nicely.
You’ll also find libraries at json.org for creating and consuming JSON in a
number of server-side languages. With these tools, it’s possible for the client and
server to send structured data back and forth as JSON over the entire course of a
web application’s user session.
Let’s return to our Hello World example for now, and see how the client handles
a JSON response.
2.2.1 Generating JSON on the server
We can go quite a long way with JSON, so let’s break it up into two parts. First,
we’re going to look at how far we can get simply by using the browser’s built-in
ability to parse JSON data, and replace the generic JavaScript response from the
previous example with a JSON object definition.
Problem
We want the server to respond to our request with rich structured data, and let the
client decide how to render the data.
Solution
Sticking with the Hello World theme, this example is going to return a fuller
description of the individual than just their name:
- The person’s initial, calculated on the server using string manipulation
- A list of things that the person likes
- Their favorite recipe, encoded as an associative array
Figure 2.5 shows the application after receiving a response.
In keeping with previous examples, the back end is going to be pretty dumb
and will, in fact, return the same data (apart from the initial) for every name. It’s
a simple step from dummy data to a real database, but we don’t want to confuse
things by introducing too many Java-specific back-end features, as the client-side
code can talk to any server-side technology.
So, first we’re going to do things the simple way and just make use of JavaScript’s
built-in JSON-parsing capabilities. Our client-side code appears in Listing 2.5.
Working with JSON in this way is pretty simple. We use eval() to parse the JSON
response , remembering to add parentheses around the string before we parse
it. Using the parsed object in our update() method is then entirely natural,
because it’s just another JavaScript object.
Let’s look briefly at the server-side code required to get us this far. Listing 2.6
shows iteration 5 of our JSP file.
As we said earlier, most of the data that we’ve generated here is dummy data.
What’s interesting to us here is the creation of the JSON string, which we’ve simply
written out by hand, inserting variable values where appropriate.
Discussion
We’ve demonstrated in this example that parsing JSON on the client is extremely
easy, and that alone makes it a compelling possibility. However, looking back at
Figure 2.4, you can see that we’ve only covered one of the four stops in the full
round-trip between client and server: the conversion of JSON to client-side
objects. For a small app like this one, what we’ve done so far is good enough, but
in larger apps, or those handling more complex data, we would want to automatically
handle all aspects of serialization and deserialization, and be free to concentrate
on business logic on the server and rendering code on the client. Before
we leave JSON, let’s run through one more example, in which we execute a full
round-trip between the client and server.
2.2.2 Round-tripping data using JSON
When we’re writing the client callback, we love JSON, because it makes everything
so simple. However, we passed the request data down to the server using a
standard HTTP query string, and then constructed the JSON response by hand.
If we could manage all communication between the browser and server using
JSON, we might save ourselves a lot of extra coding.
To get to that happy place, we’re going to have to employ a few third-party
libraries. So, let’s get coding, and see how happy we are when we’ve got there.
Problem
We want to apply JSON at all the interfaces between our application tiers and
HTTP, so that the client code can be written purely as JavaScript objects and the
server purely as Java (or PHP, .NET, or whatever) objects.
Solution
We can use Figure 2.4 as a crib sheet, to see where the gaps in our design are. On
the browser, we’ve already handled step 4, the conversion of the response text
into a JavaScript object. We still need to consider the conversion of the object into
JSON on the client, though. To do this, we’ll need to use the json.js library from
www.json.org. Listing 2.7 shows how it works.
The first thing that we need to do is include the json.js library . Once we’ve
done that, we can simplify the response-handling code by using the JSON.parse()
method . More importantly, though, we can reconsider the way we put together
the request.
So far, we’ve been sending GET requests to the server, passing in data on the
query string. This is fine for requesting data, but when we want to update information
or send a more complex request to the server, we’d be better off using a
POST request. POST requests have a body as well as a set of headers, and we can
populate that body with any text that we want. Here, we’re going to use JSON.
We’re still using Prototype to send the request, and we now pass in a postBody
property with the options to the Ajax.Request constructor. The value of this is the
result of calling JSON.stringify() . stringify() takes a JavaScript object as an
argument and recurses through it, writing it out as JSON. Thus, our POST body
will not contain URL-encoded key-value pairs, as it would if sent from an HTML
form, but a JSON string, something like this:
{ name: 'dave' }
For such a simple piece of structured data, this might be considered overkill, but
we could potentially pass very complex data in this way.
Now that we’ve figured out the client side of the solution, let’s turn to the
server. We happen to be using Java on the server for these examples, and Java
knows nothing about JSON whatsoever. Neither do most server-side languages.
So, to make sense of the response we’ve just been sending, we’ll need to bring in a
third-party library.
Whatever your server-side technology, you’re likely to find a JSON library to fit
it at www.json.org (scroll down to the bottom of the page). We selected Json-lib,
which is based on Doug Crockford’s original JSON for Java libraries.
Json-lib has quite a bit of work to do. JSON encodes structured data in a very
fluid way, and Java is a strongly typed language, so the two don’t sit together naturally.
Nonetheless, we managed to get the job done without too much trouble.
In order to use the Json-lib classes in our project, we need to import the
net.sf.json package, which we do in the <jsp:directive.page> tag . Now, on to
the code. Listing 2.8 shows the not-so-angry details.
The first challenge that we face is decoding the POST body. The Java Servlet
API, like many web technologies, has been designed to make it easy to work with
POST requests sent from HTML forms. With a JSON request body, we can’t use
HttpServletRequest.getParameter(), but need to read the JSON string in the
request via a java.io.Reader . Similar capabilities are available for other technologies.
If you’re using PHP, use the $HTTP_RAW_POST_DATA variable. If you’re
using the .NET libraries, you’ll need to get an InputStream from the HttpRequest
object, much as we’ve done here with our Java classes.
Back to the Java now. Once we’ve got the JSON string, we parse it as an object . Because of the fundamental disjoint between loosely typed JSON and strictly
typed Java, the Json-lib library has defined a JSONObject class to represent a
parsed JSON object. We can read from it using the get() method and extract
the name from the request.
Now that we’ve deserialized the incoming JSON object, we want to manipulate
it, and then send it back to the client again. The JSONObject class is able to consume
simple variable types such as strings, arrays, and Java Maps (that is, associative
arrays) , to add extra data to the object. Once we’ve modified the object, we
serialize it again , sending it back to the browser in the response.
And that’s it! We’ve now sent an object from the client, modified it on the
server, and returned it back to the client again.
Discussion
This has been the most complex example so far, demonstrating a way of communicating
structured objects back and forth over the network on top of the textbased
HTTP protocol. Because both the client and server can understand the
JSON syntax, with a little help from some libraries, we haven’t had to write any
parsing code ourselves. However, as we noted, JSON is suited for use with loosely
typed scripting languages, and so there was still some translation required. The
goal of a system like this is to be able to serialize and deserialize our domain
objects over the network. If our domain objects graph is written in Java (or C#,
say), then we still need to manually translate them into generic hashes and arrays
before they can be passed to the JSON serializer. The clumsiest piece of coding in
our round-trip was in the JSP, where we assembled the Maps and lists of data for
the JSONObject. This problem is strongly emphasized in the case of Java, which
lacks a concise syntax for defining associative arrays in particular, compared with
Ruby or PHP, for example.
From square brackets to angle brackets
There is another text-based format that can be understood by both client and
server: XML. Most server-side languages have good support for XML, so we might
find that we have an easier time working with XML than with JSON on the server.
In the next section, we’ll look at XML and Ajax, and see whether that is the case.
2.3 USING XML AND XSLT WITH AJAX
XML is a mature technology for representing structured data, supported by most
programming languages either as a core part of the language or through welltested
libraries or extensions. In the remainder of this chapter, we’ll look at how
various XML technologies work with XML, and complete our survey of basic Ajax
communication techniques.
The XMLHttpRequest object that we’ve been using for our Ajax requests has
special support for XML built into it. So far, we’ve been extracting the body of the
HTTP response as text and parsing it from there. JavaScript in the web browser
doesn’t have a standard XML parser available to it, but the XHR object can parse
XML responses for us, as we’ll see in the next example.
2.3.1 Parsing server-generated XML
So far, we’ve had the server generate HTML, JavaScript, and JSON responses for
us in various versions of our Hello World application. All of these formats are
designed to appeal to the browser rather than the server. XML, in contrast, is
often used to communicate between server processes, in a variety of technologies
ranging from RSS syndication feeds to web service protocols, such as XML-RPC
and SOAP. If we’re transmitting information from our domain objects up to the
client, then many server-side technologies provide support for serializing and
deserializing objects as XML.
In any of these scenarios, we may find that it’s easy to transmit data as XML,
from the perspective of the server. If this is going to be a useful way forward, then
we’ll also need to handle the XML on the client. We’ll start by looking at the builtin
support for XML offered by the XHR object. XML, like JSON, is a format for
exchanging structured data. The best way to compare the two is to set them the
same task, so we’ll follow the lead from the previous section and supply a list of
likes and a favorite recipe, as shown in Figure 2.6.
Problem
The server is sending structured data as XML. We need to parse this data on
the client.
Solution
The first step in handling XML on the client side is to use the XHR’s ability to
parse the response into a structured XML document. The second step is to read
(and potentially write) the parsed XML document using the W3C standard known
as the Document Object Model (DOM). In JavaScript, we already have an implementation
of the DOM for working with HTML web pages programmatically. The
good news, then, is that we can leverage these existing skills to work with XML
documents delivered by Ajax. Listing 2.9 shows the full code for version 6 of our
Hello World application.
The first step here is by far the easiest. We can retrieve the response as an XML
document object simply by reading the responseXML property rather than
responseText. We’ve then rewritten our update() function to accept the XML
object. All we need to do now is read the individual data elements from the
XML object and render the data as HTML content .
In practice, neither of these steps is difficult, but they are rather lengthy.
Using the DOM methods and properties such as getElementsByTagName(), get-Attribute(), and firstChild, we can drill down to the data we want, but we
need to do it step by step. These properties and methods are identical to the
ones that we use when working with HTML documents, so we won’t deal with
them individually here. If you’ve used the DOM to manipulate HTML, everything
should look familiar. If you haven’t, then there is plenty of information on
these methods online.
Once we have extracted the data that we need, then we simply assemble the
HTML content necessary to update the UI.
We’ve already discussed the many scenarios under which it might make sense
for the server to generate XML. To keep things simple in this example and avoid
in-depth coverage of technologies that only apply to a single programming language,
we’ve simply generated the XML by hand in our JSP. Listing 2.10 presents
the JSP for the sake of completeness.
Note that we’ve set the contentType of our response as text/xml in this case.
We’ve been doing this throughout our examples, largely as a show of good habits.
In this case, though, we have a strong practical reason for doing so. If we
don’t set the MIME type to some type of xml (either text/xml or application/xml will do), then the responseXML property of the XHR object won’t be populated
correctly.
The rest of the JSP is unremarkable. To a seasoned Java and XML coder, it
might also look overly simplistic, with the XML being handwritten as text. A more
robust solution would be to use a library like JDOM to generate the XML document
programmatically, and we encourage the reader to do that in practice.
However, we’ve left it simple here—maybe painfully simple—to show the intent
of what we’re doing without getting too deeply into Java-specific libraries. After
all, our main aim in this book is to teach client-side techniques, and our choice of
Java rather than PHP, Ruby, or .NET was essentially arbitrary.
So, getting back to the code, we’ve simply created a template of the XML document,
most of which contains dummy data, and added in a few dynamic values
along the way. Let’s move on to evaluate our experience with this example.
Discussion
Our first encounter with XML and Ajax has been rather mixed. Initially, things
looked pretty good, given the special support for XML baked into the XHR object.
However, manually walking through the XML response using the DOM was rather
lengthy and uninspiring. Experience of this sort of coding has been sufficient to
put a lot of developers off XML in favor of JSON.
The DOM is a language-independent standard, with implementations in Java,
PHP, C++, and .NET, as well as the JavaScript/web browser version that we’re
familiar with. When we look at the use of XML outside of the web browser, we find
that the DOM is not very widely used and that other XML technologies exist that
make working with XML much more palatable. Thankfully, these technologies are
available within the browser too, and we’ll see in the next section how we can use
them to make Ajax and XML work together in a much happier way.
2.3.2 Better XML handling with XSLT and XPath
The XML techniques that we saw in the previous example represent the core functionality
that is available free of charge via all implementations of the XHR object.
Working directly with the DOM is not pleasant, especially if you’re used to more
modern XML-handling technologies in other languages. The most common of
these tools are XPath queries and Extensible Stylesheet Language Transformations
(XSLT) transforms.
XPath is a language for extracting data out of XML documents. In listing 2.9,
we had to drill down through the document one node at a time. Using XPath, we
can traverse many nodes in a single line. XSLT is an XML-based templating language
that will allow us to generate any kind of content, such as, for instance,
HTML, from our XML document more easily, and also separate out the logic
from the presentation rather better than we’ve been doing so far. XSLT style
sheets (as the templates are knownno relation to Cascading Style Sheets) use
XPath internally to bind data to the presentation.
The good news is that XSLT transforms and XPath queries are available on
many browsers, specifically on Firefox and Internet Explorer. Even better, these
are native objects exposed to the JavaScript engine, so performance is good.
Safari hasn’t yet provided a native XSLT processor, so this isn’t a good option if
support for a (non-Firefox) Mac audience is important.
In the following example, we’ll let Prototype have a well-earned rest, and use
the Sarissa library to demonstrate simple cross-browser XSLT and XPath as a way
of simplifying our XML-based Hello World example.
Problem
Working with the DOM on our Ajax XML responses is slow and cumbersome.
We want to use modern XML technologies to make it easy to develop with Ajax
and XML.
Solution
Use XPath and XSLT to simplify things for you. Both Internet Explorer and
Firefox support these technologies, but in quite different ways. As with most
cross-browser incompatibilities, the best strategy is to use a third-party library
to present a unified front to our code. For this example, we’ve chosen Sarissa
(http://sarissa.sf.net), which provides cross-browser wrappers for many aspects
of working with XML in the browser. Listing 2.11 shows the client-side code for
our XSLT and XPath-powered app.
The first thing that we need to do is to import the Sarissa libraries . As well as
importing the core library, we import a support library that provides IE-style
XPath under Firefox, and a helper library that offers some convenience methods
for inserting XSLT-generated content into web pages.
Generating content using XSLT requires two XML documents from the server:
the style sheet (that is, the template) and the data. We’ll fetch the data on demand,
as before, but can load the style sheet up front when we load the app . We do this
using a DomDocument object rather than the XHR. Once again, Sarissa provides
us with a cross-browser wrapper.
To load the XML data, we will use an XHR object. Because we’ve put Prototype
aside for this example, we need to create the XHR object by hand and assign
the callback . Nonetheless, the code is simpler than we saw in chapter 1,
because we can access a native XHR object, even on Internet Explorer. Internally,
Sarissa does a bit of object detection, and if no native XHR object can be found, it
will create one for us that secretly creates an ActiveX control and uses it.
So, once we’ve got our XHR object, we can pass a DOM object to our update()
function. This was where our troubles started when using the DOM. Using XPath,
we can drill down through several layers of DOM node in a single line of code. For
example, the XPath query
/person/name/text()
selects the internal text of a <name> tag nested directly under a <person> tag at the
top of the document. XPath is too big a subject for us to tackle in depth here. We
suggest http://zvon.org as a good starting place for newcomers to XPath and
XSLT. The DOM Node methods selectSingleNode() and selectNodes() are
normally only found in Internet Explorer, but the second Sarissa library that we
loaded has provided implementations for Firefox/Mozilla. We’re using XPath to
extract the name data and the list of likes, and constructing the HTML content for
those regions of the screen manually, as they’re relatively straightforward. The
recipe section is more complex, so we’ll use that to showcase XSLT.
The final step is to perform the XSLT transform . The XSLTProcessor object
is native to Mozilla, and provided under IE by Sarissa. We pass it a reference to
the style sheet, and then call a method updateContentFromNode(). This helper
method, provided by the third Sarissa library that we loaded, will pass the data
(i.e., personNode) through the XSLT processor and write the resulting HTML into
the specified DOM node (i.e., ingrList).
Our XSL style sheet is quite straightforward. It’s a mixture of ordinary XHTML
markup, and special tags prefixed with xsl, indicating the XSL namespace. These
are treated as processing instructions. <xsl:template> tags specify chunks of content
that will be output when a node matching the XPath query in the match
attribute is encountered. <xsl:value-of> prints out data from the matched nodes,
again using XPath expressions. The <xsl:apply-templates> tag routes nodes to
other template tags for further processing. In this case, each ingredient node will
be passed to the second template from the first, generating a list.
Again, we don’t have space for a full exposition of XSLT style sheet rules here.
If you wish to know more, we recommend you visit http://zvon.org.
Finally, let’s turn briefly to the server side. The JSP used in this example is
identical to that from the previous example, as presented in listing 2.10. The only
changes that we've introduced have been in the client code.
Discussion
Using XSLT and XPath has certainly simplified our client-side XML-handling
code. In a more complex application, these technologies will scale more easily
than the DOM in terms of coding effort. We recommend that anyone considering
using Ajax with XML investigate these technologies.
In our section on JSON, we discussed the notion of round-tripping structured
data between the client and the server. Sarissa promotes this approach,
using XML as the interchange format, as it also supports cross-browser serialization
of XML objects. As we stated earlier, almost any server-side technology will
provide support for serializing and deserializing XML, too. We won’t explore a
full example here, but the principle is similar to the JSON case. When using
JSON with Java, we noted that a fair amount of manual work was required to
construct the JSON response because of the mismatch between loosely typed
JSON and strictly typed Java. The same issues exist when converting between
Java and XML, but the problem space is better understood, and out-of-the-box
solutions such as Castor and Apache XMLBeans are available.
We’ve presented Sarissa as a one-stop shop for these technologies. It isn’t the
only game in town. If you only want XPath queries, then the mozXPath.js library
(http://km0ti0n.blunted.co.uk/mozXPath.xap) provides a lightweight alternative,
with support for the Opera browser as well. And if you like the look of XSLT but
need it to work on Safari, then you can try Google’s AJAXSLT, a 100 percent Java-
Script XSLT engine (http://goog-ajaxslt.sf.net). Be warned, though, that AJAXSLT
is slow compared to the native engines in IE and Mozilla and won’t support the
full XSL namespace, so you’ll need to write your style sheets with the limitations
of the library in mind and keep them reasonably small.
We’re nearly done with our review of Ajax technologies. In the final section,
we’ll explore another Internet technology that makes use of XML, SOAP web services,
and see how Ajax can interface with that.
2.4 USING AJAX WITH WEB SERVICES
In this section, we will see how to call web services running on a remote server
over SOAP. After all, what is a web service but XML data being passed back and
forth? The XHR object is ideally suited for such a task and makes invoking remote
methods over SOAP less of a daunting task than it may seem.
Internet Explorer and the Mozilla versions of browsers all have native objects
that can be used to invoke web services. Sadly, these objects are not portable
between browsers; the developer is left to write a custom framework that can
choose the proper objects to invoke. Microsoft maintains several pages dedicated
to its version of browser-side SOAP at http://msdn.microsoft.com/workshop/author/webservice/overview.asp. Microsoft’s implementation is based on both Java-Script and VBScript. Mozilla explains their version at www.mozilla.org/projects/
webservices/; more information can also be found at http://developer.mozilla.org/en/docs/SOAP_in_Gecko-based_Browsers. Their version of browser-side SOAP is
accessible through native objects that can be constructed on the browser side.
Fortunately, there is another way. Instead of writing a high-level API that can
make use of either Internet Explorer or Mozilla objects, we can create our own
library that uses XMLHttpRequest to exchange XML, and that can parse and
generate the SOAP messages. Such a library would also allow us to run our code
on browsers that do not supply either the Microsoft or Mozilla SOAP APIs but
that do have the XHR object. The kind people at IBM have created just such a
library and have named it ws-wsajax. It can be found at www.ibm.com/developer-works/webservices/library/ws-wsajax/. We will be using this library for the remainder
of this section.
We’ve simplified the UI for this example, removing the recipe section. Passing
in the name will return a map with three entries: the name, the initial, and the list
of likes. Figure 2.7 shows the UI for this example.
This section assumes some familiarity with SOAP and SOAP-RPC. Once again,
there are several books available, as well as many good tutorials online, that cover
this topic in depth.
Problem
You need to perform SOAP-RPC from a web browser. You need to display the
resulting SOAP response as HTML.
Solution
In this section, we will write a small client using IBM’s SOAP toolkit to access our
own Hello World SOAP service, written using Apache’s Axis framework (http://
ws.apache.org/axis/). Let’s begin by defining our web service. Axis makes it very
easy to prototype web services by writing Java classes in files with a special filename
extension: .jws. Like JSPs, .jws files will be compiled on demand by a special
servlet, in this case the AxisServlet, and, while not robust enough for
production use, serve the purposes of our simple demonstration admirably. Listing
2.13 shows a simple .jws file for our Hello World service.
The class contains a single method, which will be mapped to a SOAP-RPC function.
The function takes one argument, of type String, and returns an associative
array (referred to in Java as a Map).
Pointing our browser at HelloWorld.jws will return a Web Service Description
Language (WSDL) file, which the SOAP client, such as the IBM library, can interrogate
in order to build up client-side stubs, allowing us to call the service. Listing
2.14 shows the WSDL generated by this class.
The WSDL includes details on the argument types and return types of each RPC
call, bindings to the functions, and other details needed by the client and server
to specify the nature of the interchange. Fortunately, the WSDL is generated for us
automatically by Axis and is consumed by the IBM toolkit, so we don’t need to
understand every line in it.
Let’s turn now to our client-side code. Listing 2.15 shows the full listing for
version 8 of our Hello World app.
There’s a lot going on here, so let’s take it line by line. First, we need to import the
IBM library . Because this library is built on top of Prototype, we include that
too. It relies on an older version of Prototype (v1.3.1), so we’ve renamed it to
avoid confusion with the rest of our examples .
To consume the Web Service, the first thing that we need to do is reference the
WSDL and feed it to a WS.Call object . We then extract a reference to the specific
function, as a WS.QName object . We can call this object, providing the input
parameters as a JavaScript object (which we’ve defined inline here using JSON) , and a callback function to parse the response . Parsing the response
requires a lot of node traversal. We’re working with SOAP nodes rather than DOM
nodes here, but the SOAP nodes can be converted to DOM nodes at any point.
We’ve omitted any use of XPath here to keep the example simple, but wading
through a larger SOAP response would certainly merit investigating use of XPath.
Once we have extracted the data from the response, we pass it to our update()
function , as usual. Again, we’ve opted for simplicity here, but there’s nothing
to stop you from using XSLT transforms on the SOAP response once you’ve got
ahold of it.
Discussion
We’ve shown that it’s possible to use SOAP with Ajax, provided of course that the
SOAP service is coming from the same server as the Ajax client, and therefore
honoring the browser’s same-origin security restrictions. If your back-end system
already generates SOAP, then this is a valid way of reusing existing resources.
However, we’d be tempted to say that SOAP, as an architecture for a green-field
development, is unnecessarily complex if interoperability with external entities is
not also a requirement.
The IBM SOAP toolkit made it very easy to call the service, but somewhat less easy
to parse the response. SOAP-RPC responses typically involve several namespaces
and are complex to decode. Document/literal-style SOAP bindings generally provide
simpler responses, which might be a better fit for this toolkit in production.
As always, caveat programmer. If you need a quick solution, SOAP may not be
the way to go. However, if you are creating a large application that you foresee will
require many updates and extensions, as well as integration with many aspects of
your organization, and you have the time and skills to do it, browser-side SOAP
may benefit you.
2.5 SUMMARY
By the end of chapter 1, we’d figured out how to make an Ajax request, and
looked at ways of simplifying the process by using third-party libraries. We’ve covered
a lot of ground since then and shifted our focus from simply being able to
make a request, to looking at how we want to structure the conversation between
client and server over the lifetime of the application.
We’ve looked at several techniques in this chapter and evaluated the strengths
and weaknesses of each. We began by looking at generating JavaScript code on
the server and saw the benefits of writing generated code against a high-level API
in order to prevent excessive tangling between the client and server codebases.
We moved on from there to look at ways of passing structured data between
the client and server, starting with JSON and then continuing on to XML. In each
case, we began by simply looking at how to parse the data when it arrived from
the server, and then moved on to consider the full round-trip of data between client
and server. By round-tripping the data, and having library code to serialize
and deserialize at both ends, we can free ourselves up to write business code
rather than low-level plumbing.
In contrast to JSON and XML, JSON has a closer affinity with the client side.
We struggled with our client-side XML initially but made significant advances
when we picked up XPath and XSLT. There is no clear winner between the two
technologies, and the decision remains a matter of personal taste, and depends
on whether you are integrating with legacy systems that naturally fit better with
either JSON or XML.
In the next chapter, we’ll look at JavaScript as the programmatic glue that binds
the entire Ajax app together. We’ll discuss recent advances in thinking about Java-
Script, and how they can help you to write better-structured code for your Ajax
app. We’ll conclude with a discussion of some of the popular Ajax frameworks.
|