Во время реализации RESTful API для нашей админки я столкнулся с интересной особенностью.
Как оказалось, браузер перед отправкой PUT (а также POST и DELETE) запроса к ресурсу отправляет запрос OPTIONS, ожидая, что в заголовке ответа сервера Access-Control-Allow-Methods будут указаны допустимые методы доступа к данному ресурсу. Но django-rest-framework указывает допустимые методы в заголовке Allow, который описывает методы, которые вообще можно вызывать для данного ресурса, этот заголовок не воспринимается браузерами.
Поэтому, для поддержки CORS в RESTful API необходимо написать небольшой middleware, который будет писать необходимые заголовки.
Например, это можно сделать так:
Как видно из кода, middleware просто копирует содержимое заголовка Allow в заголовок Access-Control-Allow-Methods.
Домен, который произвёл запрос сверяется со списком допустимых и записывается в Access-Control-Allow-Origin. Так нужно делать, чтобы не светить весь список допустимых доменов, а представлять потребителю только сам факт допустимости запросов с его домена.
Access-Control-Allow-Credentials позволяет браузеру пересылать куки кросс-доменно. Это потенциальное решето, но нам необходимо поддерживать legacy-авторизацию на куках CodeIgniter'а, так что пока оставили.
Всем добра и джанги.
Как оказалось, браузер перед отправкой 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'а, так что пока оставили.
Всем добра и джанги.