Дружественный URL-адрес для REST WebService с помощью CherryPy - программирование
Подтвердить что ты не робот

Дружественный URL-адрес для REST WebService с помощью CherryPy

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

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

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

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

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

4b9b3361

Ответ 1

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

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

  • Переключитесь на более явный mapper, такой как selector или routes.
  • Использовать _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-адресов в моей книге, скорее всего, будет намного проще. Очевидно, что оба могут использоваться одновременно.

Ответ 2

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