10 апр. 2014 г.

Поддержка CORS в django-rest-framework

Во время реализации RESTful API для нашей админки я столкнулся с интересной особенностью.
Как оказалось, браузер перед отправкой PUT (а также POST и DELETE) запроса к ресурсу отправляет запрос OPTIONS, ожидая, что в заголовке ответа сервера Access-Control-Allow-Methods будут указаны допустимые методы доступа к данному ресурсу. Но django-rest-framework указывает допустимые методы в заголовке Allow, который описывает методы, которые вообще можно вызывать для данного ресурса, этот заголовок не воспринимается браузерами.
Поэтому, для поддержки CORS в RESTful API необходимо написать небольшой middleware, который будет писать необходимые заголовки.
Например, это можно сделать так:
class CORSMiddleware(object):
    def process_response(self, request, response):      
        """
        Этот метод выполняется после формирования ответа клиента перед его выдачей.
        Здесь формируются заголовки ответа для поддержки CORS
        """
       
        """
        Если запросили OPTIONS, то надо скопировать допустимые методы из
        Allow в Access-Control-Allow-Methods
        """
        if request.method.upper()=='OPTIONS':          
            response["Access-Control-Allow-Methods"]=response["Allow"]
       
        #Проверяем, есть ли запрашивающий домен в списке доверенных и добавляем его в Access-Control-Allow-Origin
        if request.META.get("HTTP_ORIGIN","") in settings.ALLOW_ORIGIN:
            response["Access-Control-Allow-Origin"] = request.META.get("HTTP_ORIGIN","")
           
        #Можно пересылать куки с другого домена (дыра - лучше не используйте)
        response["Access-Control-Allow-Credentials"] = "true"      
                   
        return response


Как видно из кода, middleware просто копирует содержимое заголовка Allow в заголовок Access-Control-Allow-Methods.
Домен, который произвёл запрос сверяется со списком допустимых и записывается в Access-Control-Allow-Origin. Так нужно делать, чтобы не светить весь список допустимых доменов, а представлять потребителю только сам факт допустимости запросов с его домена.
Access-Control-Allow-Credentials позволяет браузеру пересылать куки кросс-доменно. Это потенциальное решето, но нам необходимо поддерживать legacy-авторизацию на куках CodeIgniter'а, так что пока оставили.

Всем добра и джанги.