I wrote: > In my design, I decided that making handlers according to HTTP method > was most important. Instead of using a meta class (which I know little > of) to implement this, I simply made a base class that scanned its > function attributes in __init__. A nice side effect was being able to > automatically generate a Method Not Allowed response with appropriate > Allowed header (assuming the list of accepted methods is static). Someone asked me for the code, so here it is. I think it's self-contained, except for the HttpError exception, which should be obvious. Note: while Quixote provides a table of HTTP response messages, it does not enumerate the response codes themselves (for example, METHOD_NOT_ALLOWED). So application developers are left to do this on their own :-(. -John -- http:// if le.o /
class HttpResource: """Base class for HTTP resources To handle a certain HTTP method "FOO", derived classes should have a class method called "handle_method_FOO" that takes a HTTPRequest object. If there is no handler for the requested method, then a Method Not Allowed response will be generated. The response will include the required "Allow" header. By default, this header will be set according to the defined method handler functions. In the case that the allowed methods change dynamically, list "allowed_methods" is a class instance variable that may be modified as needed. """ HANDLER_PREFIX = 'handle_method_' def __init__(self, resource_name): self.resource_name = resource_name def is_handler(attr): return attr.startswith(self.HANDLER_PREFIX) def handler_to_method(attr): return attr[len(self.HANDLER_PREFIX):] handler_names = filter(is_handler, dir(self)) self.allowed_methods = map(handler_to_method, handler_names) def __call__(self, request): """HTTP request handler""" method = request.get_environ('REQUEST_METHOD') handler = getattr(self, self.HANDLER_PREFIX + method, None) if handler: return handler(request) else: # Method Not Allowed response MUST contain Allow header (RFC-2616). # Obviously Allow should be non-null and not include the method # we are currently rejecting. assert(len(self.allowed_methods) > 0) assert(method not in self.allowed_methods) headers = { 'Allow': ', '.join(self.allowed_methods) } raise HttpError(http_status.METHOD_NOT_ALLOWED, 'Method "%s" not allowed by this resource.' % method, extra_headers = headers)