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