Publicar datos en JsonP

102

¿Es posible publicar datos en JsonP? ¿O todos los datos deben pasarse en la cadena de consulta como una solicitud GET?

Tengo muchos datos que necesito enviar al servicio, entre dominios, y es demasiado grande para enviarlos a través de la cadena de consulta.

¿Cuáles son las opciones para evitar esto?

ChrisCa
fuente

Respuestas:

83

No es posible hacer un asincrónico POSTa un servicio en otro dominio, debido a la limitación (bastante sensible) de la misma política de origen . JSON-P solo funciona porque puede insertar <script>etiquetas en el DOM, y pueden apuntar a cualquier lugar.

Por supuesto, puede convertir una página en otro dominio en la acción de un POST de forma regular.

Editar : Hay algunos trucos interesantes por ahí si estás dispuesto a hacer un gran esfuerzo para insertar mensajes ocultos <iframe>y jugar con sus propiedades.

Friedo
fuente
Mencionaste que un "POST asincrónico" no es posible ... entonces, ¿puedo hacer un POST sincrónico?
Mark
4
@mark "POST sincrónico" significa enviar un formulario que utiliza <form method = "post" action = "http: // ... / ...">
Steven Kryskalla
8
Esto no es completamente cierto. Sin duda, puede realizar POSTsolicitudes a otros dominios siempre que tanto ese dominio como su navegador sean compatibles CORS. Pero es totalmente cierto que POSTy JSONPno son compatibles.
hippietrail
2
JSONP se implementa insertando <script>etiquetas que apuntan a otro dominio. La única forma de ejecutar solicitudes POST en un navegador es mediante formularios HTML o XMLHttpRequest.
Friedo
1
(en general -) Es (!) posible hacer un POST asincrónico a un servicio en otro dominio. la limitación está en la respuesta. la limitación también está en la solicitud JSONP.
Royi Namir
20

Si necesita enviar una gran cantidad de datos entre dominios. Normalmente creo un servicio al que puedes llamar en dos pasos:

  1. Primero, el cliente envía un FORMULARIO (publicación permitida entre dominios). El servicio almacena la entrada en la sesión en el servidor (usando el GUID como clave). (el cliente crea un GUID y lo envía como parte de la entrada)

  2. Luego, el cliente realiza una inyección de script normal (JSONP) como parámetro que usa el mismo GUID que usó en la publicación FORM. El servicio procesa la entrada de la sesión y devuelve los datos en la forma JSONP normal. Después de esto, la sesión se destruye.

Esto, por supuesto, depende de que usted escriba el servidor backend.

Por
fuente
1
Probé tu enfoque. Trabajó para FF14 y Chrome20. Opera11 e IE9 simplemente no transfirieron la publicación. (Lo verificó con sus herramientas de depuración y escuchó en el servidor en el otro extremo) Tal vez esté relacionada con la discapacidad de IE esta pregunta: stackoverflow.com/questions/10395803/… Chrome se quejó en la consola, pero aún hizo el POST: XMLHttpRequest no puede load localhost: 8080 / xxx El origen nulo no está permitido por Access-Control-Allow-Origin.
OneWorld
@OneWorld: no hiciste lo que decía la respuesta. XMLHttpRequestno debería estar involucrado en absoluto. La respuesta de Per utiliza un envío de formulario regular para realizar la solicitud POST, luego una inyección de elemento de script para realizar la solicitud GET.
Quentin
7

Sé que esto es una nigromancia grave, pero pensé en publicar mi implementación de JSONP POST usando jQuery, que estoy usando con éxito para mi widget JS (esto se usa para el registro e inicio de sesión del cliente):

Básicamente, estoy usando un enfoque IFrame, como se sugiere en la respuesta aceptada. Lo que estoy haciendo de manera diferente es después de enviar la solicitud, estoy observando, si se puede acceder al formulario en el iframe, usando un temporizador. Cuando no se puede acceder al formulario, significa que la solicitud ha regresado. Luego, estoy usando una solicitud JSONP normal para consultar el estado de la operación.

Espero que alguien lo encuentre útil. Probado en> = IE8, Chrome, FireFox y Safari.

function JSONPPostForm(form, postUrl, queryStatusUrl, queryStatusSuccessFunc, queryStatusData)
{
    var tmpDiv = $('<div style="display: none;"></div>');
    form.parent().append(tmpDiv);
    var clonedForm = cloneForm(form);
    var iframe = createIFrameWithContent(tmpDiv, clonedForm);

    if (postUrl)
        clonedForm.attr('action', postUrl);

    var postToken = 'JSONPPOST_' + (new Date).getTime();
    clonedForm.attr('id', postToken);
    clonedForm.append('<input name="JSONPPOSTToken" value="'+postToken+'">');
    clonedForm.attr('id', postToken );
    clonedForm.submit();

    var timerId;
    var watchIFrameRedirectHelper = function()
    {
        if (watchIFrameRedirect(iframe, postToken ))
        {
            clearInterval(timerId);
            tmpDiv.remove();
            $.ajax({
                url:  queryStatusUrl,
                data: queryStatusData,
                dataType: "jsonp",
                type: "GET",
                success: queryStatusSuccessFunc
            });
        }
    }

    if (queryStatusUrl && queryStatusSuccessFunc)
        timerId = setInterval(watchIFrameRedirectHelper, 200);
}

function createIFrameWithContent(parent, content)
{
    var iframe = $('<iframe></iframe>');
    parent.append(iframe);

    if (!iframe.contents().find('body').length)
    {
        //For certain IE versions that do not create document content...
        var doc = iframe.contents().get()[0];
        doc.open();
        doc.close();
    }

    iframe.contents().find('body').append(content);
    return iframe;
}

function watchIFrameRedirect(iframe, formId)
{
    try
    {
        if (iframe.contents().find('form[id="' + formId + '"]').length)
            return false;
        else
            return true;
    }
    catch (err)
    {
        return true;
    }
    return false;
}

//This one clones only form, without other HTML markup
function cloneForm(form)
{
    var clonedForm = $('<form></form>');
    //Copy form attributes
    $.each(form.get()[0].attributes, function(i, attr)
    {
        clonedForm.attr(attr.name, attr.value);
    });
    form.find('input, select, textarea').each(function()
    {
        clonedForm.append($(this).clone());
    });

    return clonedForm;
}
WB
fuente
4

Bueno, generalmente JSONP se implementa agregando una <script>etiqueta al documento de llamada, de modo que la URL del servicio JSONP sea "src". El navegador obtiene la fuente del script con una transacción HTTP GET.

Ahora, si su servicio JSONP está en el mismo dominio que su página de llamadas, entonces probablemente podría improvisar algo con una simple $.ajax()llamada. Si no está en el mismo dominio, entonces no estoy seguro de cómo sería posible.

Puntiagudo
fuente
No está en el mismo dominio en este caso. Y supongo que solo es posible GET, pero quería verificarlo, ya que recién comencé a leer sobre JsonP hoy y necesito tomar algunas decisiones sobre si es adecuado para lo que necesito
ChrisCa
2
Si no está en el mismo dominio pero es compatible CORS, será posible siempre que el navegador también lo admita. En estos casos, utilizará plain en JSONlugar de JSONP.
hippietrail
Sí, @hippietrail 2 años hace una gran diferencia :-) CORS definitivamente lo hace posible, pero por supuesto requiere que la fuente de datos esté configurada de manera apropiada.
Puntiagudo
0

Puede utilizar un proxy CORS con este proyecto . Dirigiría todo el tráfico a un punto final en su dominio y transmitiría esa información a un dominio externo. Dado que el navegador registra todas las solicitudes para estar en el mismo dominio, podemos publicar JSON. NOTA: Esto también funciona con certificados SSL almacenados en el servidor.

Eugene Scray
fuente
-1

Hay una solución (pirateada) que he hecho muchas veces, podrás publicar con JsonP. (Podrá publicar un formulario, más grande que 2000 caracteres de los que puede usar con GET)

Aplicación cliente Javascript

$.ajax({
  type: "POST", // you request will be a post request
  data: postData, // javascript object with all my params
  url: COMAPIURL, // my backoffice comunication api url
  dataType: "jsonp", // datatype can be json or jsonp
  success: function(result){
    console.dir(result);
  }
});

JAVA:

response.addHeader( "Access-Control-Allow-Origin", "*" ); // open your api to any client 
response.addHeader( "Access-Control-Allow-Methods", "POST" ); // a allow post
response.addHeader( "Access-Control-Max-Age", "1000" ); // time from request to response before timeout

PHP:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Max-Age: 1000');

Al hacer esto, está abriendo su servidor a cualquier solicitud de publicación, debe volver a asegurar esto proporcionando ident o algo más.

Con este método, también puede cambiar el tipo de solicitud de jsonp a json, ambos funcionan, simplemente configure el tipo de contenido de respuesta correcto

jsonp

response.setContentType( "text/javascript; charset=utf-8" );

json

response.setContentType( "application/json; charset=utf-8" );

Por favor, no es que su servidor ya no respetará el SOP (misma política de origen), pero ¿a quién le importa?

Dimitri Kopriwa
fuente
Esto no es AJAX con CORS. AJAX implica que está utilizando XML. Este es JSON [P] con CORS. JSONP es "JSON" con "Padding". Si está enviando datos JSON, envueltos con una llamada de función para relleno, entonces es JSONP con CORS. Puede usar anotaciones de datos JSON y JSONP además de solo inyectar <script>etiquetas en su DOM HTML (diablos, incluso puede usarlas en aplicaciones de escritorio, digamos que desea realizar múltiples solicitudes JSON al mismo servidor y desea usar el nombre de la función como un ID de seguimiento de solicitud, por ejemplo).
BrainSlugs83
-6

Es posible, aquí está mi solución:

En su javascript:

jQuery.post("url.php",data).complete(function(data) {
    eval(data.responseText.trim()); 
});
function handleRequest(data){
    ....
}

En tu url.php:

echo "handleRequest(".$responseData.")";
nosemaj
fuente
11
En este caso, es muy probable que jQuery haya convertido su solicitud en Get de acuerdo con su documentación: Nota: Esto convertirá las POST en GET para solicitudes de dominio remoto. api.jquery.com/jQuery.ajax
OneWorld