Route requests based on the Accept header in Flask Route requests based on the Accept header in Flask flask flask

Route requests based on the Accept header in Flask


I wrote a decorator which does that (copying here for posterity). It's just a rough idea that could be improved further (e.g. returning 406 Not Acceptable response instead of using the default handler when there are no handlers that match given MIME type). More explanations are in the comments.

import functoolsfrom flask import Flask, request, jsonifyapp = Flask(__name__)def accept(func_or_mimetype=None):    """Decorator which allows to use multiple MIME type handlers for a single    endpoint.    """    # Default MIME type.    mimetype = 'text/html'    class Accept(object):        def __init__(self, func):            self.default_mimetype = mimetype            self.accept_handlers = {mimetype: func}            functools.update_wrapper(self, func)        def __call__(self, *args, **kwargs):            default = self.default_mimetype            mimetypes = request.accept_mimetypes            best = mimetypes.best_match(self.accept_handlers.keys(), default)            # In case of Accept: */*, choose default handler.            if best != default and mimetypes[best] == mimetypes[default]:                best = default            return self.accept_handlers[best](*args, **kwargs)        def accept(self, mimetype):            """Register a MIME type handler."""            def decorator(func):                self.accept_handlers[mimetype] = func                return func            return decorator    # If decorator is called without argument list, return Accept instance.    if callable(func_or_mimetype):        return Accept(func_or_mimetype)    # Otherwise set new MIME type (if provided) and let Accept act as a    # decorator.    if func_or_mimetype is not None:        mimetype = func_or_mimetype    return Accept@app.route('/')@accept     # Or: @accept('text/html')def index():    return '<strong>foobar</strong>'@index.accept('application/json')def index_json():    return jsonify(foobar=True)@index.accept('text/plain')def index_text():    return 'foobar\n', 200, {'Content-Type': 'text/plain'}


I know this is an old question but I ended up here looking for something similar so I hope it helps someone else.

flask_accept has the functionality to handle different Accept types through different routes.

from flask import Flask, jsonifyfrom flask_accept import acceptapp = Flask(__name__)@app.route('/')@accept('text/html')def hello_world():    return 'Hello World!'@hello_world.support('application/json')def hello_world_json():    return jsonify(result="Hello World!")if __name__ == '__main__':    app.run()

if you just want to reject requests depending on whether they are a specific data type you could also use Flask-Negotiate

from flask import Flaskfrom flask_negotiate import consumes, producesapp = Flask(__name__)@app.route('/consumes_json_only')@consumes('application/json')def consumes_json_only():    return 'consumes json only'

When one tries to access the endpoint without a valid Accept header:

$ curl localhost:5000 -IHTTP 415 (Unsupported Media Type)


You can return different response types based on the Accept header using request. Example.

if request.accept_mimetypes['application/json']:           return jsonify(<object>), '200 OK'