Route requests based on the Accept header in Python web frameworks

Question:

I have some experience with different web frameworks (Django, web.py, Pyramid and CherryPy), and I’m wondering in which one will it be easier and hopefully cleaner to implement a route dispatcher to a different “view/handler” based on the “Accept” header and the HTTP method e.g.:

Accept: application/json
POST /post/

is handled different than:

Accept: text/html
POST /post/

So the request gets routed to the particular view of the corresponding handler of the MIME “application/json” and the HTTP method “POST”.

I do know how to implement something like that in CherryPy, but I lose the use of the CherryPy tools for the internal redirection of the request because I’m calling the specific method directly instead of automagically from the dispatcher. Another option is to implement a full new dispatcher from scratch, but that’s the last option.

I’m aware of the alternative to use extensions in the url like /post.json or /post/.json, but I’m looking to keep the same url?

Asked By: cyraxjoe

||

Answers:

I’m not suite sure what you mean by “internal redirection”, but if you look at the code you can see that tools.accept is a really thin wrapper around lib.cptools.accept, which you can call from your own code easily. Hand it a list of Content-Types your server can send, and it will tell you which one the client prefers, or raise 406 if the types you emit and the types the client accepts don’t overlap.

Answered By: fumanchu

If all you are looking for is one framework that can do this easily, then use pyramid.

Pyramid view definitions are made with predicates, not just routes, and a view only matches if all predicates match. One such predicate is the accept predicate, which does exactly what you want; make view switching depending on the Accept header easy and simple:

from pyramid.view import view_config

@view_config(route_name='some_api_name', request_method='POST', accept='application/json')
def handle_someapi_json(request):
    # return JSON

@view_config(route_name='some_api_name', request_method='POST', accept='text/html')
def handle_someapi_html(request):
    # return HTML
Answered By: Martijn Pieters

I needed to do this in Django, and so I wrote a piece of middleware to make it possible: http://baltaks.com/2013/01/route-requests-based-on-the-http-accept-header-in-django

Here is the code:

# A simple middleware component that lets you use a single Django
# instance to serve multiple versions of your app, chosen by the client
# using the HTTP Accept header.
# In your settings.py, map a value you're looking for in the Accept header
# to a urls.py file.
# HTTP_HEADER_ROUTING_MIDDLEWARE_URLCONF_MAP = {
#     u'application/vnd.api-name.v1': 'app.urls_v1'
# }

from django.conf import settings

class HTTPHeaderRoutingMiddleware:

    def process_request(self, request):
        try:
            for content_type in settings.HTTP_HEADER_ROUTING_MIDDLEWARE_URLCONF_MAP:
                if (request.META['HTTP_ACCEPT'].find(content_type) != -1):
                    request.urlconf = settings.HTTP_HEADER_ROUTING_MIDDLEWARE_URLCONF_MAP[content_type]
        except KeyError:
            pass # use default urlconf (settings.ROOT_URLCONF)

    def process_response(self, request, response):
        return response
Answered By: Michael Baltaks
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.