How to dispatch requests based on dynamic URIs

644 views
Skip to first unread message

Johan Hartzenberg

unread,
Mar 3, 2015, 4:59:13 PM3/3/15
to cherryp...@googlegroups.com
I posted this question earlier today but it got eaten by the internet monster, so advance apologies if you see this twice.

I'm very new to CherryPy, just worked through the Tutorial on Friday, and ran into an issue with Routing the requests on a REST-ish basis.

I've done a proof- of concept in Bottle (because it is easy) and have implemented about 70 functions, but for numerous reasons I am not happy with Bottle.  Bottle is not bad, but I feel CherryPy is more stable and mature and the Web server eliminates me having to run a separate web server or battle with threading.

But I quickly ran into the issue with how CherryPy routes requests to various methods.  My URIs look something like this

/en/kiosk/<site_id>/foo/<foo_id>
/en/subapplication1/foo/<foo_id>/bar/<bar_id>
/en/subapplication1/foo/<foo_id>/bar/<bar_id>/special_attribute

REST-ish, no purist REST badge for me.  I think it is more important to be consistent and to do what makes sense.
Bottle have built-in routing
The point I'm trying to get at is that the above is quite inconvenient to implement with the cherrypy.popargs() decorator.

Bottle have built-in routing, with encoded tags used to identify names of parameters to assign corresponding parts of the request URI to on the handler.  I imagine that creating a decorator like that for CherryPy would not bee too hard (for someone with better Python skills than I poses) and would be useful for many people (based on the number of times it comes up in discussions).  I've asked essentially the same question on StackOverflow here: http://stackoverflow.com/questions/28774663/decorator-or-dispather-for-restful-url-routing-in-cherrypy

So if someone can help with a decorator or point me to an existing solution that would be very highly appreciated.



David Bolen

unread,
Mar 3, 2015, 6:57:29 PM3/3/15
to cherryp...@googlegroups.com
Johan Hartzenberg <jo...@webmaster.co.za> writes:

> But I quickly ran into the issue with how CherryPy routes requests to
> various methods. My URIs look something like this
>
> /en/kiosk/<site_id>/foo/<foo_id>
> /en/subapplication1/foo/<foo_id>/bar/<bar_id>
> /en/subapplication1/foo/<foo_id>/bar/<bar_id>/special_attribute
>
> REST-ish, no purist REST badge for me. I think it is more important to be
> consistent and to do what makes sense.
> Bottle have built-in routing
> The point I'm trying to get at is that the above is quite inconvenient to
> implement with the cherrypy.popargs() decorator.

Have you tried cherrypy.dispatch.RoutesDispatcher? It supports Routes
(https://routes.readthedocs.org/en/latest/) as the request mapping layer,
which lets you do the sort of definitions that I think you're looking for.
That's what I use for all of my CherryPy apps.

-- David

Johan Hartzenberg

unread,
Mar 4, 2015, 7:05:53 AM3/4/15
to cherryp...@googlegroups.com

Have you tried cherrypy.dispatch.RoutesDispatcher?  It supports Routes
(https://routes.readthedocs.org/en/latest/) as the request mapping layer,
which lets you do the sort of definitions that I think you're looking for.
That's what I use for all of my CherryPy apps.

Hi David, thank you for your reply.

I've just noticed the RoutesDispatcher and it's mapper function appears very similar to what is described in the Routes documentation.  Complete with lacking explanation of how the map decides which of the app's Class method's will be used for any matched route.  I'm tempted to assume that the "action" attribute will look for a class method with the same name?

I discovered this because, when looking for examples of implementing Routes with CheryPy, I noticed that the examples works without any "import routes" statement and instead builds a dispatch handler as part of setting up the application.


David Bolen

unread,
Mar 4, 2015, 5:26:05 PM3/4/15
to cherryp...@googlegroups.com
Johan Hartzenberg <jo...@webmaster.co.za> writes:

> I've just noticed the RoutesDispatcher and it's mapper function appears
> very similar to what is described in the Routes documentation. Complete
> with lacking explanation of how the map decides which of the app's Class
> method's will be used for any matched route. I'm tempted to assume that
> the "action" attribute will look for a class method with the same name?

Yes, though that's really a Routes question than a CherryPy question.
The RoutesDispatcher is a very thin layer on top of Routes.Mapper plus
support for the CherryPy configuration stuff - for the actual mapping,
connect() is just Routes.Mapper.connect, and URL lookup uses
Routes.Mapper.match().

Routes associates the URL lookup with a controller (the object) and an
action (the method) and any other arguments that method needs. Not
all of those elements need to be in the URL itself, as you can assign
explicit values if needed. (For example, see the example on line 4 of
"Setting up Routes" in the Routes documentation - the "/" URL has
static controller and action values)

RoutesDispatcher itself (in _cpdispatch.py) is only about 100 lines so
very easy to review if you have any questions as to how it interfaces
with Routes.

-- David

David Bolen

unread,
Mar 4, 2015, 5:35:38 PM3/4/15
to cherryp...@googlegroups.com
David Bolen <db3l...@gmail.com> writes:

> Johan Hartzenberg <jo...@webmaster.co.za> writes:
>
>> (...) Complete
>> with lacking explanation of how the map decides which of the app's Class
>> method's will be used for any matched route.

> Yes, though that's really a Routes question than a CherryPy question.

(following up to myself)

Actually, I should correct this. Routes doesn't require that action be
called as the method on controller, though that's certainly a convention.
So it is in fact the RoutesDispatcher shim that is choosing to call the
action method - and in fact, in looking at the code, it will also fall
back to calling the controller directly if no action is defined.

It's been quite a while since I first started using this, so I guess I
where the division of responsibility lay, and should have re-checked first.

-- David

Sylvain Hellegouarch

unread,
Mar 6, 2015, 5:31:12 AM3/6/15
to cherryp...@googlegroups.com
Hello Johan,

2015-03-02 10:06 GMT+01:00 Johan Hartzenberg <jo...@webmaster.co.za>:
I posted this question earlier today but it got eaten by the internet monster, so advance apologies if you see this twice.

I'm very new to CherryPy, just worked through the Tutorial on Friday, and ran into an issue with Routing the requests on a REST-ish basis.

I've done a proof- of concept in Bottle (because it is easy) and have implemented about 70 functions, but for numerous reasons I am not happy with Bottle.  Bottle is not bad, but I feel CherryPy is more stable and mature and the Web server eliminates me having to run a separate web server or battle with threading.

But I quickly ran into the issue with how CherryPy routes requests to various methods.  My URIs look something like this

/en/kiosk/<site_id>/foo/<foo_id>
/en/subapplication1/foo/<foo_id>/bar/<bar_id>
/en/subapplication1/foo/<foo_id>/bar/<bar_id>/special_attribute

REST-ish, no purist REST badge for me.  I think it is more important to be consistent and to do what makes sense.
Bottle have built-in routing
The point I'm trying to get at is that the above is quite inconvenient to implement with the cherrypy.popargs() decorator.


How come?

 

Bottle have built-in routing, with encoded tags used to identify names of parameters to assign corresponding parts of the request URI to on the handler.  I imagine that creating a decorator like that for CherryPy would not bee too hard (for someone with better Python skills than I poses) and would be useful for many people (based on the number of times it comes up in discussions).  I've asked essentially the same question on StackOverflow here: http://stackoverflow.com/questions/28774663/decorator-or-dispather-for-restful-url-routing-in-cherrypy

So if someone can help with a decorator or point me to an existing solution that would be very highly appreciated.




As David suggests, you may want to use Routes (or, in the old days, selector) but I think you can easily get away with pure CherryPy through _cp_dispatch:


Just remember that CherryPy's default dispatcher is a tree of nodes that CherryPy tries to follow when a request is made. In URLs you described, <foo_id> is an unknown node at the time CherryPy starts. This is where technics, such as described in the documentation I linked, come into play. Indeed _cp_dispatch let's you map those dynamic component at process time. It's a matter of choice: declarative-style (routes) vs imperative-style (_cp_dispatch).

Hope that helps,
--
Reply all
Reply to author
Forward
0 new messages