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 (для этого есть прослушиватель).

идеальное поведение

в идеале, я хочу это:

  1. запустите jQuery AJAX POST/PUT/DELETE запрос
  2. он должен отправить запрос опций со всеми заголовками CORS
  3. NelmioCorsBundle должен возвращать правильные заголовки CORS, принимающие запрос, даже перед запуском любого контроллера внутри приложения (сделанного прослушивателем запросов пакета)
  4. если принято, соответствующий HTTP-запрос отправляется контроллеру со всеми данными запроса в виде сериализованной строки JSON (в полезной нагрузке запроса), и Symfony извлекает его и интерпретирует правильный JSON string как объект массива
  5. контроллер получает данные, делает свое дело и возвращает application/json ответ.

но здесь я пробовал комбинации maaaaany, и я не могу заставить его работать.

точка n°3 и 4 терпят неудачу, полностью или частично.

пробовал обходные пути

  1. Когда Я не сериализовать data С JSON.stringify (см. настройка часть выше), запрос опций отправляется, но FOSRestBundle отправляет BadRequestHttpException слова Invalid json message received, и это совершенно нормально, потому что" запрос полезной нагрузки " (как видно из инструментов разработчика Chrome) является классическим application/x-www-form-urlencoded контент, даже если я указал contentType: "application/json" в запросе jQuery AJAX, тогда как это должна быть сериализованная строка JSON.

  2. однако, если я do сериализовать data var," полезная нагрузка запроса " действительна, но запрос параметров не отправляется, что приводит к сбою всего запроса из-за отсутствия принятия CORS.

  3. если я заменить 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.