tutorial symfony3 origin missing headers enable control allow jquery ajax symfony cors fosrestbundle

symfony3 - CORS con Symfony, jQuery, FOSRestBundle y NelmioCorsBundle



reason cors header access control allow origin missing symfony (1)

⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
TL; DR: Editar (antes de leer todo):

Este problema está resuelto porque cometí un error muy estúpido en mi código, no relacionado con CORS ni nada.
Si desea leer este tema de todos modos, solo tenga en cuenta que tiene una configuración de CORS que funciona , si es posible que desee tomar un buen ejemplo.

⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️

(fin de la nota)

Situación

Tengo una aplicación Symfony multidominio, y la parte de back-end actualiza datos con servicios web, no formularios clásicos, por alguna razón.

Back-end: back.mydomain.dev Webservices domain: api.mydomain.dev

Estoy usando jQuery para hacer llamadas AJAX a estos servicios web, y si quiero modificar o crear objetos, también envío solicitudes AJAX, los objetos se fusionan con entidades de Doctrine y persisten.

He estado luchando durante todo un año para hacer solicitudes GET, PUT, POST y DELETE que funcionen correctamente en esta aplicación, y solo porque están en dominios diferentes, me veo obligado a configurar CORS en mis diferentes entornos.

Preparar

Todas las solicitudes de jQuery AJAX se ven así:

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);

Detrás, las rutas se administran con Symfony.

Para la configuración de CORS , estoy usando NelmioCorsBundle con esta configuración:

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/?"

El controlador utilizado está extendiendo el de FOSRestBundle , tiene algo de seguridad (por ejemplo, no puede POST / PUT / DELETE cuando no tiene el rol correcto), puede actualizar objetos y devuelve solo datos JSON (hay un detector para eso) .

Comportamiento ideal

Idealmente, quiero esto:

  1. Ejecute la solicitud jQuery AJAX POST / PUT / DELETE
  2. Tiene que enviar una solicitud OPTIONS con todos los encabezados CORS
  3. NelmioCorsBundle debe devolver los encabezados CORS correctos aceptando la solicitud que debe realizarse, incluso antes de ejecutar cualquier controlador dentro de la aplicación (realizada por el oyente de solicitud del paquete)
  4. Si se acepta, la solicitud HTTP adecuada se envía al controlador con todos los datos de solicitud como una cadena JSON serializada (en la carga útil de la solicitud), y Symfony la recupera e interpreta la cadena JSON correcta como un objeto de matriz
  5. El controlador obtiene los datos, hace sus cosas y devuelve una respuesta de application/json .

Problema

Pero aquí, probé todas las combinaciones, y parece que no puedo hacer que funcione.

Los puntos n ° 3 y 4 están fallando, total o parcialmente.

Intentó soluciones

  1. Cuando no serializo los data con JSON.stringify (vea la parte de Configuración anterior), se envía la solicitud OPTIONS, pero FOSRestBundle envía una BadRequestHttpException dice Invalid json message received , y es totalmente normal porque la "carga útil de solicitud" (como se ve) en las herramientas para desarrolladores de Chrome) es el contenido clásico application/x-www-form-urlencoded , incluso si especifiqué contentType: "application/json" en la solicitud jQuery AJAX, mientras que debería ser una cadena JSON serializada.

  2. Sin embargo, si serializo la var de data , la "carga útil de solicitud" es válida, pero la solicitud de OPCIONES no se envía, lo que hace que toda la solicitud falle debido a la falta de aceptación de CORS.

  3. Si reemplazo contentType: "application/json" con application/x-www-form-urlencoded o multipart/form-data , y no serializo los datos, entonces, la carga útil de la solicitud es válida, pero la solicitud OPTIONS no se envía . También debería ser normal, como se explica en los documentos de jQuery en el parámetro contentType :

    Nota: Para las solicitudes de dominio cruzado, establecer el tipo de contenido en cualquier otra cosa que no sea application / x-www-form-urlencoded, multipart / form-data, o text / plain activará el navegador para enviar una solicitud de OPCIONES previas al servidor.

    Pero entonces, ¿cómo enviar una solicitud CORS correcta?

Preguntas

  • ¿De dónde viene este problema?
  • ¿Es esto un problema con mi configuración? Con jQuery?
  • Con AJAX en sí?
  • ¿Cuáles pueden ser las diferentes soluciones para resolver este maldito problema?

Edits (después de los comentarios)

2015-06-29 17:39

Condiciones:

En AJAX, contentType se establece en application/json . La opción de data se establece en una cadena JSON serializada.

Encabezados REQUEST de Preflight

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/...

Jefes de RESPUESTA antes del vuelo

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 REQUEST encabezados (después de la verificación previa)

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

Carga de solicitud (en bruto): {"json":{"id":1,"name":"object"}}

Debe señalarse que esto arroja un error de javascript:

XMLHttpRequest no puede cargar http://api.mydomain.dev/fr/object/1 . Ningún encabezado ''Access-Control-Allow-Origin'' está presente en el recurso solicitado. El origen '' http://back.mydomain.dev '' no está, por lo tanto, permitido.

POST RESPONSE encabezados (después de la verificación previa)

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

También traté de usar vainilla javascript para hacer XMLHttpRequest, el problema sigue siendo el mismo:

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"}'');

Luego, el navegador envía una solicitud de OPCIONES:

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

Que devuelve los encabezados de respuesta correctos:

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

Pero luego, el navegador envía la solicitud POST sin ningún encabezado "Access-Control- *".


En realidad, mi configuración funcionó bien.
Absolutamente perfecto, si pudiera decirlo.

Era solo una especie de ... Estupidez.

Una exit; estaba escondido en un archivo PHP.

Lo siento por molestia. Supongo que es una especie de registro en una proporción de texto / calidad en SO.

Editar: todavía espero que este problema pueda ser un ejemplo adecuado de un proyecto Symfony totalmente compatible con CORS.