Flask Snippets

Decorator for the HTTP Access Control

By Armin Ronacher filed in Decorators

Cross-site HTTP requests are HTTP requests for resources from a different domain than the domain of the resource making the request. For instance, a resource loaded from Domain A makes a request for a resource on Domain B. The way this is implemented in modern browsers is by using HTTP Access Control headers: Documentation on developer.mozilla.org.

The following view decorator implements this:

from datetime import timedelta
from flask import make_response, request, current_app
from functools import update_wrapper


def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

Unmodified this decorator requires Flask 0.8. If you want to use it with earlier versions of Flask you need to make sure to explicitly enable 'OPTIONS' as method when creating the route.

Here a basic overview of what the parameters do:

And here is how you can use it:

@app.route('/my_service')
@crossdomain(origin='*')
def my_service():
    return jsonify(foo='cross domain ftw')

Please keep in mind that with Flask 0.7 you need to explicitly provide OPTIONS:

@app.route('/my_service', methods=['GET', 'OPTIONS'])
@crossdomain(origin='*')
def my_service():
    return jsonify(foo='cross domain ftw')

This snippet by Armin Ronacher can be used freely for anything you like. Consider it public domain.

Comments