[Grok-dev] REST thinking
michael.kerrin at openapp.ie
Thu Aug 9 14:47:11 EDT 2007
On Tuesday 07 August 2007 14:27:58 Martijn Faassen wrote:
> Hi there,
> I've been doing some more thinking on REST support for Grok. In the
> interests of having some discussion, I'll sketch out the current state
> of my thinking and experiments here. I intend to have REST integrated
> into Grok within the next few weeks, and I'd appreciate feedback/help.
> Why REST in Grok?
> Why does Grok need REST support? My personal motivation: I'm writing a
> web service with Grok that primarily has a web interface. In general, I
> think Grok (and Zope 3) is well suited towards writing REST-based web
> services, but a bit more help is needed.
> What is REST?
> Very briefly, Grok exposes objects as resources that have their own
> URLs. REST is a way to make those resources respond to only 5 methods:
> GET, POST, PUT and DELETE (and a few stragglers like HEAD I'm going to
> ignore). The idea is that each of these methods does something useful in
> the context of the application. Typically that's CRUD (BREAD) like:
> CRUD: Create Read Update Delete
> BREAD: Browse Read Edit Add Delete
> Browse: GET links to objects from container, or URL representing search
> Read: GET representation of object
> Edit: PUT representation of object
> Add: POST representation of object to container or special factory URL
> Delete: DELETE object
> REST in Grok
> Basically we need to be able to respond directly to HTTP methods for a
> resource. Following our XML-RPC and JSON support, we can do this using a
> special REST view that looks like this (hypothetical code):
> class MyREST(grok.REST):
> def GET(self):
> return "the information"
> def POST(self):
> self.context['new_object'] = create(request.bodyStream)
> def PUT(self):
> def DELETE(self):
> del self.context.__parent__[self.context.__name__]
> Again like with XML-RPC, each method is turned into its own view.
> More high-level REST views
> In the case of the container in particular, it would be nice if more
> high-level REST views were available. In particular, POST needs a
> factory that can turn a representation into a new object that is then
> placed in the container. DELETE is as sketched out above. With POST a
> more complicated choreography is often needed, where a suggestion to be
> used for naming can be passed in a request header, and the response
> contains the new location of the object that is just created.
> HTTP response codes
> It would be nice if we had a good pattern for giving the right HTTP
> response codes. I still need to think about it. Probably we can just
> raise the appropriate exceptions, but in high-level views we may be able
> to automate some of this.
> Working together with browser views
> The same application should be able to have a normal web-based UI as
> well as a REST presentation. How to do this? Since Ruby on Rails
> supports this, I've studied the way they distinguish between the two.
> Initially, Rails' REST integration distinguished based on the 'Accept'
> header in the request. If Accept prefers text/xml, it would go through
> the REST code path, and if HTML is requested, it'd go through the normal
> code path.
> The drawback of this approach is that it is hard to debug using a
> browser - a browser will always get the normal views. You'll have to use
> a tool like Curl and give it command-line options.
> So, while Rails still supports this, they have now also enabled another
> way which is much easier to debug, based on extensions. It's also
> addressable using an independent URL, which is a good feature. If a
> resource is accessed with a particular extension, REST is enabled:
> http://localhost/foo -> normal Grok handling, default view
> http://localhost/foo.xml -> REST XML representation
> Note that we're talking about URLs to *resources*, not URLs to
> particular views, for REST. REST only talks to resources, not views, so
> 'foo' is an actual object stored in the database, not a view on an object.
> We can change the traversal behavior to support this. An alternative is
> to rely on skins:
> http://localhost/foo -> normal grok handling
> http://localhost/++skin++rest/foo -> REST XML representation
> Thinking about this, this seems the better way. The URLs look uglier
> (can be fixed with a rewrite rule though), but URL generation is going
> to work correctly, and no traversal hacking is required. This gives me
> an excuse to finally look into merging the skinning work. :)
> Zope 3 supports DELETE and PUT for the purposes of webdav (and other
> things). Integrating REST in Grok will probably break webdav. Is anyone
> using webdav? If not, I propose we look into fixing webdav after we
> actually complete the REST work and not worry too much about it now. If
> you are using WebDAV with Grok I expect help from you in making it work. :)
> Implementation notes
> I created a preliminary implementation. Grok already has a special
> publication integrated that takes care of the removal of security
> proxies. Besides this a new GrokRequest object is introduced that is a
> special BrowserRequest. This request object is necessary to override the
> traversal behavior to check for the REST views (GET, POST, etc). It's
> important that this check takes place *before* a default view is looked
> up - this happens in the request's traversal. If REST views are
> unavailable, the normal views are looked up.
> It may be that this design can change if we use a separate skin for REST
> always. publications have a special 'canHandle' method that can sniff (I
> think) the request to see whether the request is trying to access a REST
> skin. This might allow for a more elegant design.
> Feedback? Questions? Comments? As stated above, I'd like to make quick
> progress on implementating this in the coming weeks.
I haven't done much with Grok (yet, but plan to one of these days). But I was a bit confused about the skinning thing for a while, but I think I now see where you are going with it. If I am right then you can tidy this up further and have a URL scheme like:
localhost/++rest++atom/... then everything after the last slash in my example is the path to the resource and all views in this "skin" could return atom feeds for collections / folders and atom entries for non-folder items.
Then you can easily support multiple RESTful protocols via localhost/++rest++json/... localhost/++rest++customnamehere/... all for the same resources. Hopefully it will be easy enough to configure clients for this scheme also or you could use a rewrite rule as you suggested. I would go on to say that all views, within a "skin", correspond to the protocol you are talking about. Then you don't have to default to any normal view since it probable doesn't belong to the protocol in question.
My reasoning for this is that REST isn't a protocol. And ++skin++rest doesn't tell you anything about what language the server speaks whereas ++rest++atom does. Personally I don't think this url is any ugilier then your other suggestions mainly since it is more informative. I renamed skin -> rest is the scheme above since looking at your mail first confused me a lot as I think of a skin mainly from a portal look and fell perpestive but I can still live with ++skin++atom etc.
On the HTTP response codes I have writen z3c.conditionalviews for my work on WebDAV - it will handle all the conditional HTTP requests like If-Match, If-None-Match etc and handle the responses as best as I could figure out the HTTP spec. It hooks into the publication callObject method. Hopefully you will find this helpful. Otherwise my experience working with WebDAV was that using exceptions worked great for me.
I wouldn't worry about WebDAV, if a protocol is handled under a "skin" then it shouldn't interfere with WebDAV or any other RESTful protocol that some one may implement.
My 2 cents,
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Grok-dev