jQuery $ .ajax (), $ .post enviando “OPTIONS” como REQUEST_METHOD en Firefox

Tener problemas con lo que pensé que era un plugin jQuery relativamente simple …

El complemento debería obtener datos de un script php a través de ajax para agregar opciones a un . La solicitud de ajax es bastante genérica:

 $.ajax({ url: o.url, type: 'post', contentType: "application/x-www-form-urlencoded", data: '{"method":"getStates", "program":"EXPLORE"}', success: function (data, status) { console.log("Success!!"); console.log(data); console.log(status); }, error: function (xhr, desc, err) { console.log(xhr); console.log("Desc: " + desc + "\nErr:" + err); } }); 

Esto parece funcionar bien en Safari. En Firefox 3.5, REQUEST_TYPE en el servidor siempre es ‘OPCIONES’, y los datos $ _POST no aparecen. Apache registra la solicitud como tipo ‘OPCIONES’:

 ::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46 

¿Por qué esta llamada ajax funcionaría en Safari, pero no en Firefox, y cómo la arreglo para Firefox?

 Encabezados de respuesta
 Fecha: mié, 08 de julio de 2009 21:22:17 GMT
 Servidor: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
 X-Powered-By: PHP / 5.2.6
 Contenido-Longitud 46
 Tiempo de espera de Keep-Alive = 15, max = 100
 Conexión Keep-Alive
 Content-Type text / html

 Encabezados de solicitud
 Forma de orden de host: 8888
 User-Agent Mozilla / 5.0 (Macintosh; U; Intel Mac OS X 10.5; es-US; rv: 1.9.1) Gecko / 20090624 Firefox / 3.5
 Aceptar texto / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8
 Accept-Language en-us, en; q = 0.5
 Aceptar-Codificación gzip, desinflar
 Accept-Charset ISO-8859-1, utf-8; q = 0.7, *; q = 0.7
 Keep-Alive 300
 Conexión keep-alive
 Origen http://ux.inetu.act.org
 Access-Control-Request-Method POST
 Access-Control-Request-Headers x-requested-with

Aquí hay una imagen de la salida de Firebug:

El motivo del error es la misma política de origen. Solo le permite hacer XMLHTTPRequests en su propio dominio. Vea si puede usar una callback JSONP en su lugar:

 $.getJSON( 'http:///api.php?callback=?', function ( data ) { alert ( data ); } ); 

Utilicé el siguiente código en el lado de Django para interpretar la solicitud de OPCIONES y establecer los encabezados de control de acceso necesarios. Después de esto, mis solicitudes de dominio cruzado de Firefox comenzaron a funcionar. Como se dijo anteriormente, el navegador primero envía la solicitud de OPCIONES e inmediatamente después el POST / GET

 def send_data(request): if request.method == "OPTIONS": response = HttpResponse() response['Access-Control-Allow-Origin'] = '*' response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS' response['Access-Control-Max-Age'] = 1000 # note that '*' is not valid for Access-Control-Allow-Headers response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept' return response if request.method == "POST": # ... 

Editar: parece ser que al menos en algunos casos también necesita agregar los mismos encabezados de control de acceso a la respuesta real. Esto puede ser un poco confuso, ya que la solicitud parece tener éxito, pero Firefox no pasa el contenido de la respuesta al Javascript.

Este artículo del centro de desarrolladores de mozilla describe varios escenarios de solicitudes entre dominios. El artículo parece indicar que una solicitud POST con el tipo de contenido de ‘application / x-www-form-urlencoded’ debe enviarse como una ‘simple solicitud’ (sin solicitud ‘preflight’ OPTIONS). Sin embargo, descubrí que Firefox envió la solicitud OPTIONS, aunque mi POST se envió con ese tipo de contenido.

Pude hacer esto funcionar al crear un controlador de solicitudes de opciones en el servidor, que establece el encabezado de respuesta ‘Access-Control-Allow-Origin’ en ‘*’. Puede ser más restrictivo configurándolo en algo específico, como ‘ http://someurl.com ‘. Además, he leído que, supuestamente, puedes especificar una lista de múltiples orígenes separados por comas, pero no pude hacer que esto funcione.

Una vez que Firefox recibe la respuesta a la solicitud OPTIONS con un valor aceptable de ‘Access-Control-Allow-Origin’, envía la solicitud POST.

Solucioné este problema usando una solución totalmente basada en Apache. En mi vhost / htaccess pongo el siguiente bloque:

 # enable cross domain access control Header always set Access-Control-Allow-Origin "*" Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS" # force apache to return 200 without executing my scripts RewriteEngine On RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule .* / [R=200,L] 

Es posible que no necesite la última parte, dependiendo de lo que suceda cuando Apache ejecute el script de destino. El crédito va para el amable personal de ServerFault para la última parte.

Este PHP en la parte superior del script que responde parece funcionar. (Con Firefox 3.6.11. Todavía no he probado mucho).

 header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST, GET, OPTIONS'); header('Access-Control-Max-Age: 1000'); if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) { header('Access-Control-Allow-Headers: ' . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']); } else { header('Access-Control-Allow-Headers: *'); } if("OPTIONS" == $_SERVER['REQUEST_METHOD']) { exit(0); } 

Tuve el mismo problema al enviar solicitudes a Google Maps, y la solución es bastante simple con jQuery 1.5 – para dataType use dataType: "jsonp"

Estaba mirando a través de la fuente 1.3.2, cuando uso JSONP, la solicitud se hace construyendo un elemento SCRIPT dinámicamente, que supera la política Same-domain de los navegadores. Naturalmente, no puede realizar una solicitud POST utilizando un elemento SCRIPT, el navegador obtendría el resultado utilizando GET.

Como está solicitando una llamada JSONP, el elemento SCRIPT no se genera, porque solo lo hace cuando el tipo de llamada AJAX se establece en GET.

http://dev.jquery.com/ticket/4690

Culprit es una solicitud previa al vuelo utilizando el método OPTIONS

Para los métodos de solicitud HTTP que pueden causar efectos secundarios en los datos del usuario (en particular, para los métodos HTTP distintos de GET, o para el uso POST con ciertos tipos MIME), la especificación exige que los navegadores “revisen” previamente la solicitud, solicitando los métodos compatibles desde el servidor con un método de solicitud HTTP OPTIONS, y luego, tras la “aprobación” del servidor, el envío de la solicitud real con el método de solicitud HTTP real.

La especificación web se refiere a: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Resolví el problema agregando las siguientes líneas en Nginx conf.

  location / { if ($request_method = OPTIONS ) { add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Authorization"; add_header Access-Control-Allow-Credentials "true"; add_header Content-Length 0; add_header Content-Type text/plain; return 200; } location ~ ^/(xxxx)$ { if ($request_method = OPTIONS) { rewrite ^(.*)$ / last; } } 

Tuvimos un problema como este con ASP.Net. Nuestro IIS devolvía un error interno del servidor al intentar ejecutar jQuery $.post para obtener contenido html debido a que PageHandlerFactory estaba restringido para responder solo a los verbos GET,HEAD,POST,DEBUG . Entonces puede cambiar esa restricción agregando el verbo “OPCIONES” a la lista o seleccionando “Todos los verbos”

Puede modificar eso en su Administrador de IIS, seleccionando su sitio web, luego seleccionando Asignaciones de controlador, haga doble clic en su PageHandlerFactory para los archivos * .apx que necesite (Usamos el grupo de aplicaciones integrado con framework 4.0). Haga clic en Solicitar restricciones, luego vaya a Verbs Tabn y aplique su modificación.

Ahora nuestra solicitud de $.post está funcionando como se esperaba 🙂

Compruebe si la URL de action su formulario incluye la parte www del dominio, mientras que la página original que ha abierto se visualiza sin www .

Por lo general, hecho para Urls Canonical ..

Luché durante horas antes de tropezar con este artículo y encontré el indicio de Cross Domain.

Parece que si o.url = 'index.php' y este archivo existe está bien y devuelve un mensaje de éxito en la consola. Devuelve un error si uso url: http://www.google.com

Si realiza una solicitud posterior, ¿por qué no utiliza directamente el método $ .post ?

 $.post("test.php", { func: "getNameAndTime" }, function(data){ alert(data.name); // John console.log(data.time); // 2pm }, "json"); 

Es mucho más simple.

He publicado un ejemplo claro de cómo resolver esto si controlas el código del servidor del dominio al que estás enviando. Esta respuesta se aborda en este hilo, pero esto lo explica más claramente en IMO.

¿Cómo envío una solicitud POST entre dominios a través de JavaScript?

La solución a esto es:

  1. use dataType: json
  2. agregar &callback=? a tu url

esto funcionó al llamar a Facebook API y con Firefox. Firebug usa GET lugar de OPTIONS con las condiciones anteriores (las dos).

Otra posibilidad de eludir el problema es usar un script proxy. Ese método se describe, por ejemplo, aquí

Puedes probar esto sin

contentType:application/x-www-form-urlencoded

Intenta agregar la opción:

dataType: “json”

  function test_success(page,name,id,divname,str) { var dropdownIndex = document.getElementById(name).selectedIndex; var dropdownValue = document.getElementById(name)[dropdownIndex].value; var params='&'+id+'='+dropdownValue+'&'+str; //makerequest_sp(url, params, divid1); $.ajax({ url: page, type: "post", data: params, // callback handler that will be called on success success: function(response, textStatus, jqXHR){ // log a message to the console document.getElementById(divname).innerHTML = response; var retname = 'n_district'; var dropdownIndex = document.getElementById(retname).selectedIndex; var dropdownValue = document.getElementById(retname)[dropdownIndex].value; if(dropdownValue >0) { //alert(dropdownValue); document.getElementById('inputname').value = dropdownValue; } else { document.getElementById('inputname').value = "00"; } return; url2=page2; var params2 = parrams2+'&'; makerequest_sp(url2, params2, divid2); } }); } 

Tuve un problema similar al tratar de usar la API de Facebook.

El único contentType que no envió la solicitud Preflighted parecía ser solo text / plain … no el rest de los parámetros mencionados en mozilla aquí.

  • ¿Por qué este es el único navegador que hace esto?
  • ¿Por qué Facebook no conoce y acepta la solicitud de verificación previa?

FYI: El documento de Moz antes mencionado sugiere que los encabezados X-Lori deben desencadenar una solicitud Preflighted … no es así.

Necesitas hacer algo de trabajo en el lado del servidor. Veo que está usando PHP en el lado del servidor, pero la solución para la aplicación web .NET está aquí: No se puede establecer el tipo de contenido en ‘application / json’ en jQuery.ajax

Haz lo mismo en el script PHP y funcionará. Simplemente: Al principio, el navegador de solicitudes pregunta al servidor si está autorizado a enviar tales datos con dicho tipo y la segunda solicitud es la correcta / permitida.

Intenta agregar lo siguiente:

 dataType: "json", ContentType: "application/json", data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}), 

Utilicé una URL proxy para resolver un problema similar cuando quiero publicar datos en mi servidor de Apache alojado en otro servidor. (Esta puede no ser la respuesta perfecta pero resuelve mi problema).

Siga esta URL: Usando Mode-Rewrite para proxying , agrego esta línea a mi httpd.conf:

  RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P] 

Por lo tanto, puedo simplemente publicar datos en / solr en lugar de publicar datos en http: // ip: 8983 / solr / *. Entonces estará publicando datos en el mismo origen.

Ya tengo este código manejando bien mi situación de cors en php:

 header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN ); header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN ); header( 'Access-Control-Allow-Credentials: true' ); 

Y funcionaba bien de forma local y remota, pero no para cargar cuando estaba en modo remoto.

Algo sucede con apache / php O mi código, no me molesté en buscarlo, cuando solicitas OPCIONES devuelve mi encabezado con reglas de cors pero con 302 resultados. Por lo tanto, mi navegador no reconoce como una situación aceptable.

Lo que hice, basado en la respuesta de @Mark McDonald, es poner este código después de mi encabezado:

 if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' ) { header("HTTP/1.1 202 Accepted"); exit; } 

Ahora, cuando solicite OPTIONS , solo enviará el encabezado y el resultado 202.

Por favor tenga en cuenta:

JSONP solo es compatible con el método de solicitud GET.

* Enviar solicitud por Firefox : *

 $.ajax({ type: 'POST',//<<=== contentType: 'application/json', url: url, dataType: "json"//<<============= ... }); 

Por encima de la solicitud, envíe por OPCIONES (mientras ==> escriba: 'POST' ) !!!!

 $.ajax({ type: 'POST',//<<=== contentType: 'application/json', url: url, dataType: "jsonp"//<<============== ... }); 

Pero por encima de la solicitud, envíe por GET (mientras ==> escriba: 'POST' ) !!!!

Cuando esté en "comunicación entre dominios", preste atención y tenga cuidado.