CORS с Symfony, jQuery, FOSRestBundle и NelmioCorsBundle
⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
TL; DR: Edit (перед чтением всего):
этот вопрос решить потому что я сделал очень глупую ошибку в своем коде, не связанную с CORS или чем-то еще.
Если вы хотите прочитать эту проблему в любом случае, просто обратите внимание, что она имеет рабочая конфигурация CORS, если вы хотите взять хороший пример.
⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
(конец Примечание)
ситуация
у меня есть многодоменное приложение Symfony, а серверная часть обновляет данные с помощью веб-сервисов, а не классических форм, по некоторым причинам.
Back-end: назад.мойдомен.разработка Домен веб-сервисов: API-интерфейса.мойдомен.Дэв!--36-->
я использую jQuery для выполнения вызовов AJAX для этих веб-сервисов, и если я хочу изменить или создать объекты, я также отправляю запросы AJAX, объекты объединяются с сущностями доктрины и упорствовавший.
я боролся в течение всего года, чтобы сделать GET, PUT, POST и удалить запросы, которые будут работать должным образом в этом приложении, и только потому, что они находятся на разных доменах, я вынужден настроить CORS на моих различных средах.
настройка
все запросы jQuery AJAX выглядят следующим образом:
ajaxObject = {
url: 'http://api.mydomain.dev/' + uri,
type: method, // Can be GET, PUT, POST or DELETE only
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true,
contentType: "application/json",
jsonp: false,
data: method === 'GET' ? data : JSON.stringify(data) // Here, "data" is ALWAYS containing a plain object. If empty, it equals to "{}"
};
// ... Add callbacks depending on requests
$.ajax(ajaxObject);
позади, маршруты управляются с помощью Symfony.
на CORS настройки, Я использую NelmioCorsBundle с такой конфигурацией:
nelmio_cors:
paths:
"^/":
allow_credentials: true
origin_regex: true
allow_origin:
- "^(https?://)?(back|api).mydomain.dev/?"
allow_headers: ['Origin','Accept','Content-Type']
allow_methods: ['POST','GET','DELETE','PUT','OPTIONS']
max_age: 3600
hosts:
- "^(https?://)?(back|api).mydomain.dev/?"
используемый контроллер расширяется FOSRestBundleодин, имеет некоторую безопасность (например, не может опубликовать/поместить/удалить, когда у вас нет правильной роли), может обновлять объекты и возвращает только данные JSON (для этого есть прослушиватель).
идеальное поведение
в идеале, я хочу это:
- запустите jQuery AJAX POST/PUT/DELETE запрос
- он должен отправить запрос опций со всеми заголовками CORS
- NelmioCorsBundle должен возвращать правильные заголовки CORS, принимающие запрос, даже перед запуском любого контроллера внутри приложения (сделанного прослушивателем запросов пакета)
- если принято, соответствующий HTTP-запрос отправляется контроллеру со всеми данными запроса в виде сериализованной строки JSON (в полезной нагрузке запроса), и Symfony извлекает его и интерпретирует правильный JSON string как объект массива
- контроллер получает данные, делает свое дело и возвращает
application/json
ответ.
но здесь я пробовал комбинации maaaaany, и я не могу заставить его работать.
точка n°3 и 4 терпят неудачу, полностью или частично.
пробовал обходные пути
Когда Я не сериализовать
data
СJSON.stringify
(см. настройка часть выше), запрос опций отправляется, но FOSRestBundle отправляетBadRequestHttpException
словаInvalid json message received
, и это совершенно нормально, потому что" запрос полезной нагрузки " (как видно из инструментов разработчика Chrome) является классическимapplication/x-www-form-urlencoded
контент, даже если я указалcontentType: "application/json"
в запросе jQuery AJAX, тогда как это должна быть сериализованная строка JSON.однако, если я do сериализовать
data
var," полезная нагрузка запроса " действительна, но запрос параметров не отправляется, что приводит к сбою всего запроса из-за отсутствия принятия CORS.-
если я заменить
contentType: "application/json"
Сapplication/x-www-form-urlencoded
илиmultipart/form-data
, и не сериализуйте данные, тогда полезная нагрузка запроса действительна, но запрос опций не отправляется. Это также должно быть нормально, как объяснено в документах jQuery под contentType:Примечание: для кросс-домен запросы, устанавливая тип контента на что-либо, кроме application/x-www-form-urlencoded, multipart/form-data или text/plain, вызовет браузер для отправки запроса параметров предполетной обработки на сервер.
но тогда, как отправить правильный запрос CORS?
вопросы
- откуда берется эта проблема?
- это проблема с моей установки? С jQuery?
- с AJAX себя?
- какие могут быть различные решения для решения этой проклятой проблемы?
правки (после комментариев)
2015-06-29 17:39
условия:
в AJAX,contentType
задано значение application/json
.
The data
параметр имеет значение сериализованной строки JSON.
заголовки запроса перед полетом
OPTIONS http://api.mydomain.dev/fr/object/1 HTTP/1.1
Access-Control-Request-Method: POST
Origin: http://back.mydomain.dev
Access-Control-Request-Headers: accept, content-type
Referer: http://back.mydomain.dev/...
заголовки ответов перед полетом
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, DELETE, PUT, OPTIONS
Access-Control-Allow-Headers: origin, accept, content-type
Access-Control-Max-Age: 3600
Access-Control-Allow-Origin: http://back.mydomain.dev
POST
Заголовки запросов (после предполетной подготовки)
POST http://api.mydomain.dev/fr/object/1 HTTP/1.1
Origin: http://back.mydomain.dev
Content-Type: application/json
Referer: http://back.mydomain.dev/...
Cookie: mydomainPortal=v5gjedn8lsagt0uucrhshn7ck1
Accept: application/json, text/javascript, */*; q=0.01
запрос полезной нагрузки (raw):
{"json":{"id":1,"name":"object"}}
необходимо отметить, что это вызывает ошибку javascript:
XMLHttpRequest не может загрузить http://api.мойдомен.dev/fr/object / 1. Заголовок "Access-Control-Allow-Origin" отсутствует на запрашиваемом ресурсе. Происхождение'http://back.мойдомен.Дэв!--66--> ' поэтому доступ запрещен.
POST
ответ заголовки (после предполетной подготовки)
HTTP/1.1 200 OK
Date: Mon, 29 Jun 2015 15:35:07 GMT
Server: Apache/2.4.12 (Unix) OpenSSL/1.0.2c Phusion_Passenger/5.0.11
Keep-Alive: timeout=5, max=91
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
2015-06-29 17:39
я также попытался использовать vanilla javascript для создания XMLHttpRequest, проблема все та же:
var xhr = new XMLHttpRequest;
xhr.open('POST', 'http://api.mydomain.dev/fr/objects/1', true);
xhr.setRequestHeader('Content-type', 'application/json');
xhr.withCredentials = true; // Sends cookie
xhr.onreadystatechange = function(e) {
// A simple callback
console.info(JSON.parse(e.target.response));
};
// Now send the request with the serialized payload:
xhr.send('{"id":1,"name":"Updated test object"}');
затем браузер отправляет запрос параметров:
OPTIONS http://api.mydomain.dev/fr/objects/1 HTTP/1.1
Connection: keep-alive
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Origin: http://back.mydomain.dev
, который возвращает правильные заголовки ответа:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, DELETE, PUT, OPTIONS
Access-Control-Allow-Headers: origin, accept, content-type
Access-Control-Max-Age: 3600
Access-Control-Allow-Origin: http://back.mydomain.dev
но затем браузер отправляет запрос POST без заголовка" Access-Control -*".
1 ответов
на самом деле, моя установка работала хорошо.
Абсолютно идеально, если можно так выразиться.
Это было что-то вроде... Тупость.
An exit;
был скрыт глубоко в файле PHP.
извините за беспокойство. Я думаю, это своего рода запись в соотношении текст/качество на SO.
Edit: я все еще надеюсь, что эта проблема может быть правильным примером полностью совместимого с CORS проекта Symfony.