Przyjazny adres URL dla odpoczynku WebService z CherryPy

Robię RESTful WebService używając CherryPy 3 ale napotykam problem : Chcę móc odpowiadać na takie prośby jak : /customers / 1/products / 386 czyli chcę cały produkt o ID 386 Klienta o ID 1.

Więc staram się zrobić to metodą CherryPy ' ego tak:

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)

Ale kiedy żądam /customers/1/products / 386, zamiast przekierowywać mnie do ProductController.Uzyskać odpowiednie parametry, przekierowuje mnie do UserController.GET z parametrami 1, "produkty", 386.

Do przekierowania do ProductController.GET muszę zapytać / klienci/produkty / 386 co jest nieprawidłowe, ponieważ brakuje mi parametru ID użytkownika.

Widziałem na tej prezentacji: RESTful web Applications with CherryPy że styl ścieżki, którego chcę użyć, wydaje się być dobrym wyborem. Ale czy istnieje łatwy sposób na wdrożenie go z Cherry Py ?

Słyszałem o metodzie _cp_dispatch z CherryPy 3 ale nie rozumiem dokładnie co to jest i jak go używać. Czy zastępuje MethodDispatcher ?

Author: Yohan D, 2013-04-03

2 answers

Cherry używa mapera opartego na drzewie, który nie pasuje do segmentów, które nie mają fizycznej rzeczywistości jako obiekt Pythona, tutaj twój / 1 / segment.

Mając to na uwadze, CherryPy zapewnia funkcjonalności, aby osiągnąć twój cel.

  • zamiana na bardziej wyraźny maper, taki jak selektor lub trasy.
  • Użyj _cp_dispatch
  • Użyj cherrypy.popargs
Skupmy się na dwóch ostatnich.

_cp_dispatch jest specjalną metodą, którą zadeklaruj w dowolnym kontrolerze, aby masować pozostałe segmenty, zanim CherryPy je przetworzy. Dzięki temu możesz usunąć, dodać lub w inny sposób obsłużyć dowolny segment, a nawet całkowicie zmienić pozostałe części.

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 jest prostszy, ponieważ nadaje nazwę każdemu segmentowi, którego CherryPy nie byłby w stanie zinterpretować inaczej. To sprawia, że dopasowanie segmentów z podpisami obsługi strony jest łatwiejsze i pomaga CherryPy zrozumieć struktura adresu 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())

W obu przypadkach przejdź do http://whatevertomakesohappy.com:8080/nirvana / a potem http://whatevertomakesohappy.com:8080/nirvana/albums/nevermind/

Oba są potężne, ale który z nich chcesz użyć, zależy od Ciebie. W przypadku prostych adresów URL popargs prawdopodobnie będzie znacznie łatwiejszy w mojej książce. Oczywiście oba mogą być używane jednocześnie.

 21
Author: Sylvain Hellegouarch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-03 14:24:23

Dzięki za odpowiedź. Doprowadziłeś mnie do odpowiedzi, której szukałem. Ja używałem takiego routera:

    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']))
 4
Author: Yohan D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-04 09:10:49