[Grok-dev] Re: Is there an easy way to customize URL used for grok.REST (use suffix instead of prefix) ?

Martijn Faassen faassen at startifact.com
Mon Jun 2 08:46:36 EDT 2008


Craeg Strong wrote:
> Using the excellent REST tutorial I was able to create a REST adapter 
> that returns XML content in response to
> an HTTP "GET" request at 
> http://localhost:8080/++rest++xml/myapp/mycontainer
> 
> However, I would like to use simple relative URLs in my javascript like 
> 'xml'
> 
> Ext.onReady(function() {
> 
>    var proxy  = new Ext.data.HttpProxy({ url: 'xml', method:'GET'});
> 
> In my browser-side code, it seems to be much easier to construct a 
> relative url simply by appending a suffix (such as 'xml') to the URL, 
> rather than parsing the URL and inserting a prefix in between the 
> host+port and path
> 
> The documentation provides a tantalizing hint: "As you can see, you need 
> to use the ++rest++<protocolname> pattern somewhere in the URL in order 
> to access the REST view for your objects. If you don't like the ++rest++ 
> bit you can also provide (directlyProvides) the layer manually to the 
> request during traversal"
> 
> But I am having trouble figuring out how to marry grok.Traverser with 
> grok.REST.
> 
> Can anyone provide a bread crumb for me to follow?

Okay, your use case is a bit different than the one I wrote the hint 
for. I've seen something close to this use case before though (hi 
Jasper). The idea is that you can do the following:

@grok.subscribe(MyApp, IBeforeTraverseEvent)
def restSkin(obj, event):
   if not MyLayer.providedBy(event.request)
         zope.publisher.http.applySkin(event.request,
                                       MyLayer, grok.IRESTSkinType)

(note that the import path of applySkin changes in Grok 0.13, to be 
released, due to some changes in Zope 3, but the principle will be the same)

This says that before you traverse into MyApp, the MyLayer rest protocol 
needs to be applied to the request (if it's not already supplied).

The problem in your case is that you want *part* of your application to 
be restful, and parts presumably just show the original UI. This is what 
the whole skin/rest protocol system is for - you can really reliably 
split both domains from each other.

I agree though that it's frustrating to have to insert the ++rest++foo 
bit in the URL space and that it'd be nicer to construct relative URLs. 
You could see this as partitioning your resource space. Some resources 
(you call it 'xml') are REST-only, others are non-REST.

You could accomplish something like this by using traversing behavior 
(you can use a custom grok.Traverser on an interface to make this more 
universal):

class MyContainer(grok.Container):
     def traverse(self, name):
         if name == 'xml':
             return ContainerXml(self)

class ContainerXml(grok.Model):
     def __init__(self, context):
         self.context = context

@grok.subscribe(ContainerXml, IBeforeTraverseEvent)
def restSkin(obj, event):
    ... apply the rest skin

You then attach your REST view to ContainerXml, which can talk to the 
container through its context.

I *think* this should work; the only bit I'm a bit unsure about is when 
IBeforeTraverseEvent is applied here; it might confuse things. An 
alternative is to set the skin in the 'traverse' method in MyContainer 
(you need a grok.Traverser subclass for that, actually, as you need 
access to the request).

If this pattern is common enough, we might want to abstract this whole 
pattern out into something like a 'RelativeRestResource' or something. I 
can imagine people would want something similar for JSON and XMLRPC as 
well, though it's less important to keep the URL spaces separate in that 
case.

Regards,

Martijn



More information about the Grok-dev mailing list