The Web itself is an example of REST architecture. You can look at any web site as a set of resources whose state can be retrieved and updated using HTTP methods like GET and POST. Thus a web application doesn't have to support special web service protocols like XML-RPC or SOAP for you to be able to work with it from a client program: straight HTTP is all you need.
5.2.1. How Do I Do That?
Use the twisted.web.client.getPage function to send HTTP requests to web resources. Despite its name, getPage supports HTTP methods beyond GET, as demonstrated in Example 5-2.
Example 5-2. rest_client.py
from twisted.web import client import urllib class RestResource(object): def _ _init_ _(self, uri): self.uri = uri def get(self): return self._sendRequest('GET') def post(self, **kwargs): postData = urllib.urlencode(kwargs) mimeType = 'application/x-www-form-urlencoded' return self._sendRequest('POST', postData, mimeType) def put(self, data, mimeType): return self._sendRequest('PUT', data, mimeType) def delete(self): return self._sendRequest('DELETE') def _sendRequest(self, method, data="", mimeType=None): headers = {} if mimeType: headers['Content-Type'] = mimeType if data: headers['Content-Length'] = str(len(data)) return client.getPage( self.uri, method=method, postdata=data, headers=headers) class RestWikiTester(object): def _ _init_ _(self, baseUri): self.baseUri = baseUri def test(self): return self.createPage("RestTestTmp").addCallback( lambda _: self.deletePage("RestTestTmp")).addCallback( lambda _: self.createPage("RestTest")).addCallback( lambda _: self.getPage("RestTest")).addErrback( self.handleFailure) def createPage(self, page): print "Creating page %s..." % page return RestResource(self.baseUri + page).put( "Created using HttpPut!", "text/html") def deletePage(self, page): print "Deleting page %s..." % page return RestResource(self.baseUri + page).delete( ) def getPage(self, page): uri = self.baseUri + page return RestResource(uri).get( ).addCallback( self._gotPage, uri) def _gotPage(self, pageData, uri): print "Got representation of %s:" % uri print pageData def handleFailure(self, failure): print "Error:", failure.getErrorMessage( ) if __name__ == "_ _main_ _": from twisted.internet import reactor tester = RestWikiTester('http://localhost:8082/') tester.test( ).addCallback(lambda _: reactor.stop( )) reactor.run( )
When you run rest_client.py, it creates and deletes a page called RestTestTmp, creates a page called RestTest, and then displays the contents of RestTest as rendered by the server:
$ python rest_client.py Creating page RestTestTmp... Deleting page RestTestTmp... Creating page RestTest... Got representation of http://localhost:8082/RestTest:
RestTest
<a href="/edit/RestTest">Edit Page</a> <a href="/index">Index</a> <a href="/">Home</a>
Created using <a href="HttpPut">HttpPut</a>!
5.2.2. How Does That Work?
The RestResource class takes a URI and provides methods for sending HTTP GET, PUT, POST, and DELETE requests to that resource. All of these use twisted.web.client.getPage to send the request. The post method will take an arbitrary number of keyword arguments, URL-encode them, and use the resulting string as the body of the request sent to the server. The put method will take arbitrary data representing the new state of the resource, along with a MIME type identifying the data format. The other methods, get and delete, don't need to pass any additional information.
The RestWikiTester class has a test method that chains together a series of tests to be run on the server. While not a comprehensive test, it demonstrates the use of the GET, PUT, and DELETE methods on the Wiki application from Example 5-1.
Getting Started
Building Simple Clients and Servers
Web Clients
Web Servers
Web Services and RPC
Authentication
Mail Clients
Mail Servers
NNTP Clients and Servers
SSH
Services, Processes, and Logging