CORS

I recently learned about CORS while creating an API for a web app. It stands for Cross-origin resource sharing. This is a magical new browser spec which defines a way for a web server (in this case an API) to talk to another web site on a different domain. In the past we’ve used various other techniques like JSONP and iFrames to get around this issue. Not anymore.

Here’s a code snippet I used to enable CORS in my django API.

Create a middleware class in django called /rest/middleware.py and tweak the ALLOWED_* constants to your needs:

import re

from django.utils.text import compress_string
from django.utils.cache import patch_vary_headers

from django import http

ALLOWED_ORIGINS = 'http://mydomain.com'
ALLOW_CREDENTIALS = 'true'
ALLOWED_METHODS = 'POST, GET, OPTIONS, PUT, DELETE'
ALLOWED_HEADERS = 'Origin, X-Requested-With, Content-Type, Accept'

class CORSMiddleware(object):
    """
        This middleware allows cross-domain XHR using the html5 postMessage API.

        eg.         
        Access-Control-Allow-Origin: http://foo.example
    """
    def process_request(self, request):

        if 'HTTP_ACCESS_CONTROL_REQUEST_METHOD' in request.META:
            response = http.HttpResponse()
            response['Access-Control-Allow-Origin'] = ALLOWED_ORIGINS
            response['Access-Control-Allow-Credentials'] = ALLOW_CREDENTIALS
            response['Access-Control-Allow-Methods'] = ALLOWED_METHODS
            response['Access-Control-Allow-Headers'] = ALLOWED_HEADERS
            return response

        return None

    def process_response(self, request, response):
        # Avoid unnecessary work
        if response.has_header('Access-Control-Allow-Origin'):
            return response

        response['Access-Control-Allow-Origin'] = ALLOWED_ORIGINS
        response['Access-Control-Allow-Credentials'] = ALLOW_CREDENTIALS
        response['Access-Control-Allow-Methods'] = ALLOWED_METHODS
        response['Access-Control-Allow-Headers'] = ALLOWED_HEADERS
        return response

Then just add that to your settings.py:

.
.
MIDDLEWARE_CLASSES = (
    .
    .
    'rest.middleware.CORSMiddleware'
    )
.
.

That’s it! Also, http://enable-cors.org/ is a great resource if you are looking for code samples and guidelines on how to go about implementing CORS for other languages/platforms.

But wait, not too fast. As always, IE stands in the way of creating elegant web applications. IE6 and IE7 both lack CORS support, while IE8 and IE9 have broken implementations. IE10 is the only version with a non-buggy CORS implementation. If you want to achieve cross browser compatibility you would have to fall back to JSONP which is very limited and only supports GET requests or use some iFrame magic like I talked about in my previous blog post.

Luckily I was able to find a better option. A bit more Googling let me to this JavaScript library which lets you seamlessly make cross-browser cross-domain AJAX requests across the board without major hacks. So how does it do it? You can read this in-depth explanation on his website but basically it relies on CORS for modern browsers which support it while using flXHR - a cross-domain AJAX shim (written in Flash+JS) on older browsers as a fallback to essentially achieve the same goal.

So here’s to writing more elegant APIs and web applications!

If you have any questions or comments, please post them below. If you liked this post, you can share it with your followers or follow me on Twitter!