Using REST with Ajaxby Nic Ferrier
This article shows how to use Ajax techniques to make web apps with REST APIs.
Everyone's talking about REST these days. Lots of people are still struggling with it, and there's good reason for that--REST is actually quite difficult to fit into the browser-based HTML Web, for two reasons:
- Current HTML forms support only
- HTML forms always involve a page change
As far as I'm concerned, this is fair enough; REST is primarily a web services platform, an alternative to CORBA, SOAP, and XMLRPC, not a user interface. Still, at least some of the time, it's nice to use a REST API as the foundation for a web app. On the other hand, Ajax can be really complicated, and it always seems to involve lots of different bits of code.
That is the point of this article. REST's clean definition of an application's architecture solves at least some of the problems with Ajax; and using Ajax to a REST web app leaves the REST API uncomplicated and pure.
The best way for me to explain this is to refer to a real REST API. I recently built a multiuser ATOM/RSS aggregator. Each user can have a list of feeds (URLs, basically) and manipulate those list items. I built a simple REST API for managing the lists.
The feed aggregator needs to:
- add a feed to the user's list
- list the current feeds
- remove a feed from the user's list
Clearly I could model the list of a user's feeds as a resource:
... and each feed as a subresource of that:
That is the great thing about REST: it's easy to work out the details of a pure REST implementation:
/feedwill add a feed to the user's list. The
POSTwill have to include a parameter specifying the feed URL.
/feedwill list the feeds. A nice HTML display of the feeds might be appropriate.
/feed/67will remove the feed with that ID.
This is all very simple, and simplicity is of course why REST is so powerful.
This code is pretty easy to do with just about any web server and language combination. I used mod_python.
Here's the main handler and
GET method implementation:
import os import re import urllib from mod_python import apache from mod_python import util def handler(http): if http.method == "GET": return get(http) elif http.method == "POST": return post(http) elif http.method == "DELETE": return delete(http) return apache.HTTP_NOT_ACCEPTABLE def get(http): """Display a list of feeds.""" username = http.user form = util.FieldStorage(http) def feeds(): try: dbcon = PgSQL.connect(http.get_options()["DBURL-" + http.hostname]) curs = dbcon.cursor() curs.execute("select id, url from get_feeds('%s')" % (username)) result = curs.fetchone() while result != None: yield result result = curs.fetchone() dbcon.close() except: pass return http.content_type = "text/html" xmlout(feeds, http) return apache.OK def xmlout(generator, out): """Use 'generator' to get the data and send it to 'out'""" print >>out, "<?xml version="1.0"?>" print >>out, "<?xsl-stylesheet href='/display-feed.xslt' version="1.0"?>" print >>out, "<feeds>" for id,url in generator(): print >>out, "<feed><id>%s</id><url>%s</url></feed>" % (id, re.sub("&", "&", url)) print >>out, "</feeds>"
GET returns an HTML page of the user's feeds. Notice that there is an XML representation in the code, so I could add content negotiation to the mix to support both XML and HTML output.