16

我正在使用 CherryPy 3 制作一个 RESTful WebService,但我遇到了一个问题:我希望能够回答如下请求: /customers/1/products/386这意味着我想要 ID 为 1 的客户端的 ID 为 386 的所有产品。

所以我尝试使用 CherryPy 的 MethodDispatcher 来实现,如下所示:

class UserController(object):
    exposed = True

    def __init__(self):
        self.product = ProductController()

    @log_io
    def GET(self, *args):
        return "GET Users :" + str(args)


class ProductController(object):
    exposed = True
    @log_io
    def GET(self, *args):
        return "GET Product :" + str(args)

但是当我请求 /customers/1/products/386 时,它没有将我重定向到具有正确参数的 ProductController.GET,而是将我重定向到带有参数 1、“产品”、386 的 UserController.GET。

要重定向到 ProductController.GET 我必须查询 /customers/products/386 这是不正确的,因为我错过了用户 ID 参数。

我在此演示文稿中看到:带有 CherryPy 的 RESTful Web Applications,我想使用的路径样式似乎是一个不错的选择。但是有没有一种简单的方法可以用 Cherry Py 来实现它?

我听说过 CherryPy 3 的 _cp_dispatch 方法,但我不知道它是什么以及如何使用它。它会取代 MethodDispatcher 吗?

4

2 回答 2

24

CherryPy 使用基于树的映射器,它不能很好地适应没有物理现实的段作为 Python 对象,这里是 /1/ 段。

话虽如此,CherryPy 确实提供了实现目标的功能。

  • 切换到更明确的映射器,例如selectorroutes
  • 使用 _cp_dispatch
  • 使用cherrypy.popargs

让我们关注最后两个。

_cp_dispatch 是您在任何控制器中声明的一种特殊方法,用于在 CherryPy 处理剩余段之前对其进行处理。这使您能够删除、添加或以其他方式处理您希望的任何部分,甚至完全更改剩余部分。

import cherrypy

class Band(object):
    def __init__(self):
        self.albums = Album()

    def _cp_dispatch(self, vpath):
        if len(vpath) == 1:
            cherrypy.request.params['name'] = vpath.pop()
            return self

        if len(vpath) == 3:
            cherrypy.request.params['artist'] = vpath.pop(0)  # /band name/
            vpath.pop(0) # /albums/
            cherrypy.request.params['title'] = vpath.pop(0) # /album title/
            return self.albums

        return vpath

    @cherrypy.expose
    def index(self, name):
        return 'About %s...' % name

class Album(object):
    @cherrypy.expose
    def index(self, artist, title):
        return 'About %s by %s...' % (title, artist)

if __name__ == '__main__':
    cherrypy.quickstart(Band())

cherrypy.popargs 更直接,因为它为 CherryPy 无法以其他方式解释的任何段命名。这使得段与页面处理程序签名的匹配更容易,并帮助 CherryPy 理解 URL 的结构。

import cherrypy

@cherrypy.popargs('name')
class Band(object):
    def __init__(self):
        self.albums = Album()

    @cherrypy.expose
    def index(self, name):
        return 'About %s...' % name

@cherrypy.popargs('title')
class Album(object):
    @cherrypy.expose
    def index(self, name, title):
        return 'About %s by %s...' % (title, name)

if __name__ == '__main__':
    cherrypy.quickstart(Band())

在这两种情况下,请访问http://whatevertomakesohappy.com:8080/nirvana/,然后访问 http://whatevertomakesohappy.com:8080/nirvana/albums/nevermind/

两者都很强大,但你想使用哪一个取决于你。对于简单的 URL,popargs 在我的书中可能要容易得多。显然两者可以同时使用。

于 2013-04-03T13:57:19.770 回答
5

感谢您的回答西尔万。你让我找到了我正在寻找的答案。我像这样使用 RouteDispatcher :

    self.connect("cust_products", "/customers/{cust_id}/products/",
                 controller=CustomerController(),
                 action='index',
                 conditions=dict(method=['GET']))

    self.connect("cust_products", "/customers/{cust_id}/products/{id}",
                 controller=CustomerController(),
                 action='show',
                 conditions=dict(method=['GET']))

    self.connect("cust_products", "/customers/{cust_id}/products/",
                 controller=CustomerController(),
                 action='create',
                 conditions=dict(method=['POST']))

    self.connect("cust_products", "/customers/{cust_id}/products/{id}",
                 controller=CustomerController(),
                 action='update',
                 conditions=dict(method=['PUT']))


    self.connect("cust_products", "/customers/{cust_id}/products/{id}",
                 controller=CustomerController(),
                 action='delete',
                 conditions=dict(method=['DELETE']))
于 2013-04-04T09:10:49.953 回答