Дружественный URL для веб-сервиса REST с CherryPy

Я делаю RESTful WebService, используя CherryPy 3, но я сталкиваюсь с проблемой : Я хочу иметь возможность отвечать на такие запросы, как : / клиенты/1/продукты / 386 означает, что я хочу весь продукт с ID 386 клиента с ID 1.

поэтому я пытаюсь сделать это с MethodDispatcher CherryPy следующим образом:

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.Получите с правильными параметрами, он перенаправляет меня в UserController.Получаем с параметрами 1, "продукты", 386.

для перенаправления на ProductController.GET мне нужно запросить /customers/products / 386, что неверно, потому что я пропускаю параметр user ID.

Я видел на этой презентации : RESTful веб-приложений с CherryPy что стиль пути, который я хочу использовать, кажется хорошим выбором. Но есть ли простой способ реализовать его с помощью Cherry Py ?

Я слышал о _cp_dispatch метод CherryPy 3, но я не понимаю, что это такое и как его использовать. Заменяет ли он MethodDispatcher ?

2 ответов


CherryPy использует древовидный картограф, который не подходит для сегментов, которые не имеют физической реальности в качестве объекта Python, здесь ваш / 1 / сегмент.

С учетом сказанного, CherryPy предоставляет функциональные возможности для достижения вашей цели.

  • Swap для более явного картографа, такого как селектор или маршруты.
  • использовать _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, вероятно, будет намного проще в моей книге. Очевидно, что и то и другое можно использовать одновременно.


Спасибо за ваш ответ Сильвен. Вы привели меня к ответу, который я искал. Я использовал 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']))