Re: Re: Simple RESTful API - make a patch
"Joseph S. Tate" <[email protected]> Tue, 15 Nov 2016 06:43:56 +0000
| Newsgroups | gmane.comp.python.cherrypy |
|---|---|
| Message-ID | <CA+WDQbHScbUZTdpUUeWj=xm9FXCXm8Yu_CaLu1s-XM7+2w+OHw@mail.gmail.com> |
Ideally you'd submit a pull request from your branch with a test that proves that the fix works. Perhaps by copying and modifying a test for PUT or POST. Then I'll merge it in. Joseph On Mon, Nov 14, 2016 at 11:24 AM Kearney Taaffe <[email protected]> wrote: > @Josheph, sorry, my response wasn't very explicit. Changing the line 315 > in cherrypy._cprequest.py from > > methods_with_bodies = ('POST', 'PUT') > > > to > > methods_with_bodies = ('POST', 'PUT', 'PATCH') > > > worked. > > I posted the new PATCH() method to show that the body and route param were > printed to the log > > > > On Thursday, November 10, 2016 at 10:10:32 AM UTC-6, Kearney Taaffe wrote: > > I'm making an API in CherryPy. I must admit, it's super easy, and I'd like > to turn this example into something that the CherryPy community puts on > it's website as a "how to API" > > The problem I'm having is how to handle an HTTP PATCH request. The > following code fails with the error for HTTP PATCH requests > > AttributeError: 'Request' object has no attribute 'json' > > > Here's my main.py > import cherrypy > > > from controllers.userController import UserController > > > > > def CORS(): > """Allow web apps not on the same server to use our API > """ > cherrypy.response.headers["Access-Control-Allow-Origin"] = "*" > cherrypy.response.headers["Access-Control-Allow-Headers"] = ( > "content-type, Authorization, X-Requested-With" > ) > > cherrypy.response.headers["Access-Control-Allow-Methods"] = ( > 'GET, POST, PUT, DELETE, OPTIONS' > ) > > if __name__ == '__main__': > """Starts a cherryPy server and listens for requests > """ > > userController = UserController() > > cherrypy.tools.CORS = cherrypy.Tool('before_handler', CORS) > > cherrypy.config.update({ > 'server.socket_host': '0.0.0.0', > 'server.socket_port': 8080, > 'tools.CORS.on': True, > }) > > > # API method dispatcher > # we are defining this here because we want to map the HTTP verb to > # the same method on the controller class. This _api_user_conf will > # be used on each route we want to be RESTful > _api_conf = { > '/': { > 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), > } > } > > > # _api_user_conf better explained > # The default dispatcher in CherryPy stores the HTTP method name at > # :attr:`cherrypy.request.method<cherrypy._cprequest.Request.method>`. > > > # Because HTTP defines these invocation methods, the most direct > # way to implement REST using CherryPy is to utilize the > # :class:`MethodDispatcher<cherrypy._cpdispatch.MethodDispatcher>` > # instead of the default dispatcher. To enable > # the method dispatcher, add the > # following to your configuration for the root URI ("/"):: > > > # '/': { > # 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), > # } > > > # Now, the REST methods will map directly to the same method names on > # your resources. That is, a GET method on a CherryPy class implements > # the HTTP GET on the resource represented by that class. > > > # http://cherrypy.readthedocs.org/en/3.2.6/_sources/progguide/REST.txt > > > cherrypy.tree.mount(userController, '/api/users', _api_conf) > > > > cherrypy.engine.start() > cherrypy.engine.block() > > > > Here's the user controller (controllers/userController.py) (*NOTE: be > sure the file __init__.py is in the controllers directory and is blank)* > > import cherrypy > > > # from services.userServiceProvider import UserServiceProvider > > > from typing import Dict, List > > > ''' > NOTES > + @cherrypy.tools.json_out() - automatically outputs response in JSON > + @cherrypy.tools.json_in() - automatically parses JSON body > ''' > class UserController(): > > > # expose all the class methods at once > exposed = True > > def __init__(self): > # create an instance of the service provider > # self.userService = UserServiceProvider() > pass > > > ''' > This code allows for our routes to look like > http://example.com/api/users/uuid > and the uuid will be made available to the routes like the user input > http://example.com/api/users?uuid=uuid > ''' > def _cp_dispatch(self, vpath: List[str]): > > # since our routes will only contain the GUID, we'll only have 1 > # path. If we have more, just ignore it > if len(vpath) == 1: > cherrypy.request.params['uuid'] = vpath.pop() > > return self > > > @cherrypy.tools.json_out() > def GET(self, **kwargs: Dict[str, str]) -> str: > """ > Either gets all the users or a particular user if ID was passed in. > By using the cherrypy tools decorator we can automagically output > JSON > without having to using json.dumps() > """ > > > # our URI should be /api/users/{GUID}, by using _cp_dispatch, > this > # changes the URI to look like /api/users?uuid={GUID} > > if 'uuid' not in kwargs: > # if no GUID was passed in the URI, we should get all users' > info > # from the database > # results = self.userService.getAllUsers() > results = { > 'status' : 'getting all users' > } > else: > # results = self.userService.getUser(kwargs['uuid']) > results = { > 'status' : 'searching for user ' + kwargs['uuid'] > } > > > return results > > @cherrypy.tools.json_in() > @cherrypy.tools.json_out() > def POST(self): > """Creates a new user > """ > input = cherrypy.request.json > inputParams = {} > > # convert the keys from unicode to regular strings > for key, value in input.items(): > inputParams[key] = str(value) > > try: > # result = self.userService.addUser(inputParams) > result = { > 'status' : 'inserting new record' > } > > > if len(inputParams) == 0: > raise Exception('no body') > except Exception as err: > result = {'error' : 'Failed to create user. ' + err.__str__()} > > > return result > > @cherrypy.tools.json_out() > def DELETE(self, **kwargs: Dict[str, str]): > # convert the keys from unicode to regular strings > uuid = '' > if 'uuid' not in kwargs: > result = { > 'success' : False, > 'message' : 'You must specfy a user.' > } > > > return result > > > uuid = kwargs['uuid'] > > > try: > if len(uuid) == 0: > raise Exception('must pass in user ID') > > # result = self.userService.deleteUser(inputParams) > result = { > 'status' : 'deleting user with ID: ' + uuid > } > except Exception as err: > result = {'error' : 'could not delete. ' + err.__str__()} > > > return result > > > @cherrypy.tools.json_in() > @cherrypy.tools.json_out() > def PUT(self): > # get the request body > data = cherrypy.request.json > print('BODY:\n' + str(data)) > # result = self.userService.updateUser(data) > result = { > 'status' : 'updating user' > } > > > return result > > > @cherrypy.tools.json_in() > @cherrypy.tools.json_out() > def PATCH(self, **kwargs: Dict[str, str]): > > > # the _cp_dispatch() method > if 'uuid' not in kwargs: > result = { > 'success' : False, > 'message' : 'You must specfy a user.' > } > > > return result > else: > print('found uuid: ' + kwargs['uuid']) > > > # get the request body > data = cherrypy.request.json > > # result = self.userService.updateUser(data, kwargs['uuid']) > result = { > 'status' : 'patching user ({})'.format(kwargs['uuid']) > } > > > return result > > > def OPTIONS(self): > return 'Allow: DELETE, GET, HEAD, OPTIONS, POST, PUT' > > > def HEAD(self): > return '' > > > > By the way, feel to kang this code all you want. And, if I'm doing > something wrong, please tell me. > > -- > You received this message because you are subscribed to the Google Groups > "cherrypy-users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to cherrypy-users+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/[email protected] > To post to this group, send email to cherrypy-users-/JYPxA39Uh5TLH3MbocFF+G/[email protected] > Visit this group at https://groups.google.com/group/cherrypy-users. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "cherrypy-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to cherrypy-users+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/[email protected] To post to this group, send email to cherrypy-users-/JYPxA39Uh5TLH3MbocFF+G/[email protected] Visit this group at https://groups.google.com/group/cherrypy-users. For more options, visit https://groups.google.com/d/optout.