Search code examples
pythoncherrypy

Combining REST dispatcher with the default one in a single CherryPy app


I'm trying to make CherryPy to handle request to /api via cherrypy.dispatch.MethodDispatcher() and all other request (like /) to some default dispatcher.

After reading CherryPy's doc I have no idea how to do this. They use both routing methods only separately but this is such a basic thing that I believe it has to work together.

#!/usr/local/bin/python2.7
import cherrypy


class Root(object):
    @cherrypy.expose
    def index(self):
        return 'Hello world'

class RestAPI(object):
    @cherrypy.expose
    def POST(self, blah):
        return 'ok'

cherrypy.config.update({
    'global': {
        'environment': 'production',
        'server.socket_host': '127.0.0.1',
        'server.socket_port': 8080,
    }
})


root = Root()
root.api = RestAPI()

conf = {
    '/api': {
        'request.dispatch': cherrypy.dispatch.MethodDispatcher()
    }
}

cherrypy.quickstart(root, '', config=conf)

By calling curl 'http://localhost:8080/' it gives me Hello world which is correct.
But calling curl -X POST 'http://localhost:8080/api' returns just 404.

By the way, the're eaxctly the same question without any answer CherryPy MethodDispatcher with multiple url paths.


Solution

  • Finally I solved it. The weird thing was that I had to expose index method (and all other methods in Root class) using annotation @cherrypy.expose and not just by setting exposed = True like in RestAPI class. I don't know why.

    To properly test POST handler I didn't have to pass any variables but still I had to set Content-length: 0 header.

    class Root(object):
    
        @cherrypy.expose
        def index(self):
            return 'Hello world'
    
    
    class RestAPI(object):
    
        exposed = True
    
        def POST(self):
            return 'post'
    
        def GET(self):
            return 'get'
    
    
    cherrypy.config.update({
        'global': {
            'environment': 'test_suite',
            'server.socket_host': '127.0.0.1',
            'server.socket_port': 8080,
        }
    })
    
    cherrypy.tree.mount(Root())
    
    cherrypy.tree.mount(RestAPI(), '/api',
        {'/':
            {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}
        }
    )
    
    cherrypy.engine.start()
    cherrypy.engine.block()
    

    Proper way to test POST using cURL:

    curl -X POST --header "Content-length: 0" http://localhost:8080/api