[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