65
65
def render(self, req):
66
66
if req.method == 'GET':
67
67
outjson = self.GET(req)
68
# Since PATCH isn't yet an official HTTP method, we allow users to
69
# turn a PUT into a PATCH by supplying a special header.
68
70
elif req.method == 'PATCH' or (req.method == 'PUT' and
69
71
'X-IVLE-Patch-Semantics' in req.headers_in and
70
72
req.headers_in['X-IVLE-Patch-Semantics'].lower() == 'yes'):
71
73
outjson = self.PATCH(req, cjson.decode(req.read()))
72
74
elif req.method == 'PUT':
73
75
outjson = self.PUT(req, cjson.decode(req.read()))
76
# POST implies named operation.
77
elif req.method == 'POST':
78
# TODO: Check Content-Type and implement multipart/form-data.
79
opargs = dict(cgi.parse_qsl(req.read()))
81
opname = opargs['ivle.op']
84
raise BadRequest('No named operation specified.')
87
op = getattr(self, opname)
88
except AttributeError:
89
raise BadRequest('Invalid named operation.')
91
if not hasattr(op, '_rest_api_callable') or \
92
not op._rest_api_callable:
93
raise BadRequest('Invalid named operation.')
95
# Find any missing arguments, except for the first one (self).
96
argspec = inspect.getargspec(op)
97
missing = frozenset(argspec[0][1:]) - frozenset(opargs.keys())
99
raise BadRequest('Missing arguments: ' + ', '.join(missing))
101
outjson = op(**opargs)
103
raise MethodNotAllowed()
76
105
req.content_type = self.content_type
77
106
if outjson is not None:
78
107
req.write(cjson.encode(outjson))
110
def named_operation(meth):
111
'''Declare a function to be accessible to HTTP users via the REST API.
113
meth._rest_api_callable = True
81
116
class XHTMLView(BaseView):
83
118
A view which provides a base class for views which need to return XHTML