views:

212

answers:

4

What's the best approach of creating a RESTful web api in CherryPy? I've been looking around for a few days now and nothing seems great. For Django it seems that are lots of tools to do this, but not for CherryPy or I am not aware of them.

Later edit: How should I use Cherrypy to transform a request like /getOrders?account=X&type=Y into something like /orders/account/type ?

+1  A: 

AFAIK, for CherryPy, no such thing is available / needed. As default dispatcher is RESTful as it is and You can easily write your own, method-based for APIs and as CP is not bound to any ORM, what would You need framework for?

Almad
hyperboreean
Well, I guess something like class Account(object): def type(self): return "hello" type.exposed = True class Orders(object): account = Account() class Root(object): orders = Orders() cherrypy.quickstart(Root))? (EDIT: dah, no code formatting in comment) Follow tutorial...
Almad
A: 

I assume you've tried partial matches as talked about in the tutorial. I find that while not great, it does get the job done most of the time.

Beyond that, though I haven't tried it, Cherrypy apparently supports Routes (see http://www.cherrypy.org/wiki/PageHandlers), which gives you all kinds of RESTful options.

+1  A: 

I don't if it's the "best" way, but here's how I do it:

import cherrypy

class RESTResource(object):
   """
   Base class for providing a RESTful interface to a resource.

   To use this class, simply derive a class from it and implement the methods
   you want to support.  The list of possible methods are:
   handle_GET
   handle_PUT
   handle_POST
   handle_DELETE
   """
   @cherrypy.expose
   def default(self, *vpath, **params):
      method = getattr(self, "handle_" + cherrypy.request.method, None)
      if not method:
         methods = [x.replace("handle_", "")
            for x in dir(self) if x.startswith("handle_")]
         cherrypy.response.headers["Allow"] = ",".join(methods)
         raise cherrypy.HTTPError(405, "Method not implemented.")
      return method(*vpath, **params);

class FooResource(RESTResource):
    def handle_GET(self, *vpath, **params):
        retval = "Path Elements:<br/>" + '<br/>'.join(vpath)
        query = ['%s=>%s' % (k,v) for k,v in params.items()]
        retval += "<br/>Query String Elements:<br/>" + \
            '<br/>'.join(query)
        return retval

class Root(object):
    foo = FooResource()

    @cherrypy.expose
    def index(self):
        return "REST example."

cherrypy.quickstart(Root())

You simply derive from the RESTResource class and handle whichever RESTful verbs you desire (GET, PUT, POST, DELETE) with a method of the same name prefixed with handle_. If you do not handle a particular verb (such as POST) the base class will raise a `405 Method Not Implemented' error for you.

The path items are passed in vpaths and any query strings are passed in in params. Using the above sample code, if you were to request /foo/bar?woo=hoo, vpath[0] would be bar, and params would be {'woo': 'hoo'}.

carpie
A: 

To answer your second question, you want to define and expose a default method:

class getOrders(Object):
    def default(account, type):
        ...

    default.exposed = True

using this method, getOrders/x/y would map to default(account='x', type='y'). Like someone else said, it's not great, but it gets the job done.

As far as RESTful applications goes, I'm pretty sure the default page handler will work for such an application.

colinmarc