[Grok-dev] REST branch ready for review

Martijn Faassen faassen at startifact.com
Tue Oct 16 08:57:18 EDT 2007


Hi there,

I'd like people to take a look at the REST branch before I merge it.

The branch is:

svn+ssh://svn.zope.org/repos/main/grok/branches/faassen-rest

What this branch does is add support for REST protocol implementations. 
Following a suggestion by Michael Kerrin such a REST protocol is exposed 
as a special kind of skin-like thing. This allows us to separate REST 
requests from other requests on an objects, so that you can have a 
normal UI with views on a set of objects in parallel to the 
implementation of one or more REST protocols.

First, let's see how you define a REST protocol. Similar to the way 
skins work, first you need to define a layer:

class AtomPubLayer(grok.IRESTLayer):
    pass

We now can put a REST object in this layer:

class MyREST(grok.REST):
     grok.context(MyContainer)

     def GET(self):
         return "GET request, retrieve container listing"

     def POST(self):
         return "POST request, add something to container"

     def PUT(self):
         return "PUT request, replace complete contents"

     def DELETE(self):
         return "DELETE request, delete this object entirely"

To get to the body of the request, there's a special attribute on 'self' 
that contains this as a string:

class MyREST2(grok.REST):
     def POST(self):
         return "This is the body: " + self.body

This body should be parsed accordingly by your REST protocol implementation.

To actually issue REST requests over a URL, you need to define a REST 
protocol that uses this layer:

class AtomPubProtocol(grok.RESTProtocol):
    grok.layer(AtomPubLayer)
    grok.name('atompub') # a nicer name

Now you can access the object with the REST protocol, through requests 
like this (issuing GET, POST, PUT or DELETE):

http://localhost:8080/++rest++atompub/mycontainer

If you don't like the ++rest++ bit you can also provide the layer 
manually to the request during traversal (just like with skins), or of 
course institute a few rewrite rules in Apache.

If you don't actually use a layer for a REST subclass, it'll use the 
grok.IRESTLayer by default. This layer is the base of all REST layers.

Security works with all this: you can use @grok.require() on the REST 
methods to shield them from public use.

What I don't like about this branch is that I have to handle GET and 
POST differently than PUT and DELETE. GET and POST are handled by the 
BrowserPublication, while PUT and DELETE fall back on the HTTP 
publication. I tried to hide this from the end user and so far the only 
visible difference is that a content type of text/plain is sent back in 
case of errors with GET and POST and not in the case of PUT and DELETE.

Implementation-wise, I wish there were a better way. Normally you'd just 
register another kind of publisher to handle REST requests, analogous to 
how XMLRPC requests work. Unfortunately there is nothing in the 
enviroment of a REST request (before the request itself is born) to 
recognize that it *is* a REST request. We don't want there to be, as 
it's important a REST protocol implementation can be browsed using a 
normal web browser without having to set special headers and such (as 
this would seriously hamper debuggability). Still, it would be nice if 
there were a cleaner way to implement this.

When you review the branch, make sure to read the test code in 
ftests/rest/rest.py

Regards,

Martijn



More information about the Grok-dev mailing list