CherryPy and RESTful web api

18,262

Solution 1

I don't know 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'}.

Solution 2

Because HTTP defines these invocation methods, the most direct way to implement REST using CherryPy is to utilize the MethodDispatcher instead of the default dispatcher.

More can be found in CherryPy docs: http://cherrypy.readthedocs.io/en/latest/tutorials.html#tutorial-7-give-us-a-rest

Here is also detailed description on how to send and receive JSON using CherryPy Tools: http://tools.cherrypy.org/wiki/JSON

Solution 3

So you want to transform /getOrders?account=X&type=Y into something like /orders/account/type using Cherrypy.

I would try the approach used in http://cherrypy.readthedocs.org/en/latest/tutorial/REST.html as mentioned by @Tomasz Blachowicz, with some modifications.

Remember that you can handle something like /order/account/type with

@cherrypy.expose
def order(account=None, type=None):
    print account, type

class Root(object):
    pass

root = Root()
root.orders = orders


cherrypy.quickstart(root, '/')

So if you take the example given in http://cherrypy.readthedocs.org/en/latest/tutorial/REST.html, you can modify it to handle that type of URL.

class Orders(object):
    exposed = True
    def __init__(self):
        pass

    def GET(self, account=None, type=None):
        #return the order list for this account type
        return getOrders(account, type)

    def PUT(self, account=None, type=None, orders=None):
        #Set the orders associated with account or something
        setOrders(account, type, orders)


class Root(object):
    pass

root = Root()
root.orders = Orders()

conf = {
    'global': {
        'server.socket_host': '0.0.0.0',
        'server.socket_port': 8000,
    },
    '/': {
        'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
    },
}

cherrypy.quickstart(root, '/', conf)

Why you would want to set orders using that put method I don't know, but it does give an another example of how to do PUT methods. All you have to do is replace the method used by a request with PUT and it will use the PUT() method of Orders and use a regular GET on Orders and it will use the GET() method. Because a POST() method is not defined, POST can't be used with this example. If you try POST or DELETE you will get "405 Method Not Allowed".

I like this approach because it is easy to see what is going on and, I believe, it answers your question.

Solution 4

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.

Solution 5

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.

Share:
18,262

Related videos on Youtube

hyperboreean
Author by

hyperboreean

Updated on August 18, 2021

Comments

  • hyperboreean
    hyperboreean over 2 years

    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 ?

  • Sebastian Wagner
    Sebastian Wagner over 6 years
    Last link is gone now.

Related