Re: Simple RESTful API - make a patch
Kearney Taaffe <[email protected]> Mon, 14 Nov 2016 08:24:58 -0800 (PST)
| Newsgroups | gmane.comp.python.cherrypy |
|---|---|
| Message-ID | <[email protected]> |
@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.