diff --git a/README.rst b/README.rst index db75719..3bef411 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,58 @@ +This is a modified version of itty.py which attempts to add support for the HTTP 'Accept:' request header. + +example code: + +:: + + @get('/') + @accept('text/plain') + def index(request): + return "Hello, World!\n" + + @get('/') + @accept('text/xml') + def index(request): + return 'Hello, World!\n' + + @get('/') + @accept('application/json') + def index(request): + return '{ "greeting": "Hello, World!" }\n' + + @get('/') + def index(request): + return "Hello, World!\n" + + +output: + +:: + + $ wget -q -O - --header='Accept: text/plain' http://localhost:8080 + Hello, World! + + $ wget -q -O - --header='Accept: text/xml' http://localhost:8080 + Hello, World! + + $ wget -q -O - --header='Accept: application/json' http://localhost:8080 + { "greeting": "Hello, World!" } + + $ wget -q -O - --header='Accept: text/html' http://localhost:8080 + Hello, World! + + +This is just a first stab to flesh out the idea. + +Limitations (i.e. things which could be improved upon): + +* You have to define your methods in order from most specific to least specific. +* Wildcards and 'q' aren't supported yet. + +Feel free to take this idea and run with it. Have fun! +Jason Pepas +jason@pepas.com + + ======= itty.py ======= diff --git a/itty.py b/itty.py index d4d179b..4656ff3 100644 --- a/itty.py +++ b/itty.py @@ -312,16 +312,37 @@ def handle_error(exception, request=None): return not_found(request, exception) +def method_supports_mime_type_in_accept_header(method, accept_string): + """Determine if the MIME types supported by the method match those in the Accept header.""" + if hasattr(method, 'accepted_mime_types') == False: + # if an @accept() decorator hasn't been used, we assume */* + return True + + parts = accept_string.split(',') + mime_types_accepted_by_requester = [part.split(';')[0] for part in parts] + for mime_type in mime_types_accepted_by_requester: + if mime_type in method.accepted_mime_types: + return True + return False + + def find_matching_url(request): """Searches through the methods who've registed themselves with the HTTP decorators.""" if not request.method in REQUEST_MAPPINGS: raise NotFound("The HTTP request method '%s' is not supported." % request.method) - for url_set in REQUEST_MAPPINGS[request.method]: - match = url_set[0].search(request.path) + for method in REQUEST_MAPPINGS[request.method]: + match = method.re_url.search(request.path) - if match is not None: - return (url_set, match.groupdict()) + if match == None: + continue + + if hasattr(request, 'HTTP_ACCEPT') == True: + if method_supports_mime_type_in_accept_header(method, request.HTTP_ACCEPT) == False: + continue + + url_set = (method.re_url, method.url, method) + return (url_set, match.groupdict()) raise NotFound("Sorry, nothing here.") @@ -405,8 +426,9 @@ def get(url): """Registers a method as capable of processing GET requests.""" def wrapped(method): # Register. - re_url = re.compile("^%s$" % add_slash(url)) - REQUEST_MAPPINGS['GET'].append((re_url, url, method)) + method.url = url + method.re_url = re.compile("^%s$" % add_slash(url)) + REQUEST_MAPPINGS['GET'].append(method) return method return wrapped @@ -415,8 +437,9 @@ def post(url): """Registers a method as capable of processing POST requests.""" def wrapped(method): # Register. - re_url = re.compile("^%s$" % add_slash(url)) - REQUEST_MAPPINGS['POST'].append((re_url, url, method)) + method.url = url + method.re_url = re.compile("^%s$" % add_slash(url)) + REQUEST_MAPPINGS['POST'].append(method) return method return wrapped @@ -425,8 +448,9 @@ def put(url): """Registers a method as capable of processing PUT requests.""" def wrapped(method): # Register. - re_url = re.compile("^%s$" % add_slash(url)) - REQUEST_MAPPINGS['PUT'].append((re_url, url, method)) + method.url = url + method.re_url = re.compile("^%s$" % add_slash(url)) + REQUEST_MAPPINGS['PUT'].append(method) new.status = 201 return method return wrapped @@ -436,8 +460,9 @@ def delete(url): """Registers a method as capable of processing DELETE requests.""" def wrapped(method): # Register. - re_url = re.compile("^%s$" % add_slash(url)) - REQUEST_MAPPINGS['DELETE'].append((re_url, url, method)) + method.url = url + method.re_url = re.compile("^%s$" % add_slash(url)) + REQUEST_MAPPINGS['DELETE'].append(method) return method return wrapped @@ -451,6 +476,16 @@ def wrapped(method): return wrapped +def accept(mime_type): + """Specifies which MIME types a method handles.""" + def wrapped(method): + if method.__dict__.has_key('accepted_mime_types') is False: + method.accepted_mime_types = [] + method.accepted_mime_types.append(mime_type) + return method + return wrapped + + # Error handlers @error(403)