Postar dados no JsonP

102

É possível postar dados no JsonP? Ou todos os dados devem ser passados ​​na querystring como uma solicitação GET?

Tenho muitos dados que preciso enviar para o serviço, entre domínios, e são muito grandes para enviar por meio da string de consulta

Quais são as opções para contornar isso?

ChrisCa
fonte

Respostas:

83

Não é possível fazer POSTum serviço assíncrono em outro domínio, devido à limitação (bastante sensível) da política de mesma origem . JSON-P só funciona porque você tem permissão para inserir <script>tags no DOM e elas podem apontar para qualquer lugar.

Você pode, é claro, transformar uma página em outro domínio na ação de um POST de formulário regular.

Edit : Existem alguns hacks interessantes por aí se você estiver disposto a se esforçar muito para inserir programas ocultos <iframe>e mexer com suas propriedades.

Friedo
fonte
Você mencionou que um "POST assíncrono" não é possível ... então posso fazer um POST síncrono?
Marcar
4
@mark "POST síncrono" significa enviar um formulário que usa <form method = "post" action = "http: // ... / ...">
Steven Kryskalla
8
Isso não é bem verdade. Você certamente pode fazer POSTsolicitações a outros domínios, desde que esse domínio e seu navegador sejam compatíveis CORS. Mas é totalmente verdade que POSTe JSONPnão são compatíveis.
hippietrail
2
JSONP é implementado inserindo <script>tags que apontam para outro domínio. A única maneira de executar solicitações POST em um navegador é por meio de formulários HTML ou XMLHttpRequest.
Friedo
1
(em geral -) É (!) possível fazer um POST assíncrono para um serviço em outro domínio. a limitação está na resposta. a limitação também está na solicitação JSONP.
Royi Namir
20

Se você precisar enviar muitos dados entre domínios. Normalmente crio um serviço que você pode chamar em duas etapas:

  1. Primeiro, o cliente faz um envio de FORM (postagem permitida entre domínios). O serviço armazena a entrada na sessão no servidor (usando o GUID como chave). (o cliente cria um GUID e o envia como parte da entrada)

  2. Em seguida, o cliente faz uma injeção de script normal (JSONP) como um parâmetro que você usa o mesmo GUID que você usou na postagem do FORM. O serviço processa a entrada da sessão e retorna os dados no estilo JSONP normal. Depois disso, a sessão é destruída.

É claro que isso depende de você escrever o back-end do servidor.

Por
fonte
1
Tentei sua abordagem. Funcionou para FF14 e Chrome20. Opera11 e IE9 simplesmente não transferiram a postagem. (Verifiquei com suas ferramentas de depuração e escutei no servidor na outra extremidade) Talvez relacionada à deficiência do IE esta pergunta: stackoverflow.com/questions/10395803/… Chrome reclamação no console, mas ainda fez o POST: XMLHttpRequest não pode load localhost: 8080 / xxx Origin null não é permitido por Access-Control-Allow-Origin.
OneWorld
@OneWorld - Você não fez o que dizia a resposta. XMLHttpRequestnão deveria estar envolvido. A resposta de Per usa um envio de formulário regular para fazer a solicitação POST e, em seguida, uma injeção de elemento de script para fazer a solicitação GET.
Quentin
7

Sei que isso é uma necromancia séria, mas pensei em postar minha implementação de JSONP POST usando jQuery, que estou usando com sucesso para meu widget JS (usado para registro e login do cliente):

Basicamente, estou usando uma abordagem IFrame, conforme sugerido na resposta aceita. O que estou fazendo de diferente é depois de enviar a requisição, estou verificando, se o formulário pode ser acessado no iframe, usando um timer. Quando o formulário não pode ser alcançado, significa que o pedido foi devolvido. Então, estou usando uma solicitação JSONP normal para consultar o status da operação.

Espero que alguém ache útil. Testado em> = IE8, Chrome, FireFox e 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
fonte
4

Bem, geralmente JSONP é implementado adicionando uma <script>tag ao documento de chamada, de forma que a URL do serviço JSONP seja o "src". O navegador busca a origem do script com uma transação HTTP GET.

Agora, se seu serviço JSONP estiver no mesmo domínio que sua página de chamada, então você provavelmente poderia remendar algo com uma simples $.ajax()chamada. Se não estiver no mesmo domínio, não tenho certeza de como seria possível.

Pontudo
fonte
Não está no mesmo domínio neste caso. E estou assumindo que apenas GET é possível, mas gostaria de verificar, pois só comecei a ler sobre JsonP hoje e preciso tomar algumas decisões para saber se ele é adequado para o que eu preciso
ChrisCa
2
Se não estiver no mesmo domínio, mas for compatível CORS, será possível, desde que o navegador também ofereça suporte. Nesses casos, você usará simples em JSONvez de JSONP.
hippietrail
Sim, @hippietrail 2 anos faz uma grande diferença :-) CORS definitivamente torna isso possível, mas é claro que requer que a fonte de dados seja configurada apropriadamente.
Pointy
0

Você pode usar um proxy CORS usando este projeto . Ele direcionaria todo o tráfego para um ponto de extremidade em seu domínio e retransmitiria essas informações para um domínio externo. Como o navegador está registrando todas as solicitações para estar no mesmo domínio, podemos postar JSON. NOTA: Isso também funciona com certificados SSL mantidos no servidor.

Eugene Scray
fonte
-1

Existe uma solução (hack) que fiz várias vezes, você poderá postar com JsonP. (Você poderá postar o formulário, com mais de 2.000 caracteres do que você pode usar em GET)

Javascript do aplicativo cliente

$.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');

Fazendo assim, você está abrindo seu servidor para qualquer solicitação de postagem, você deve protegê-la novamente fornecendo ident ou outra coisa.

Com este método, você também pode alterar o tipo de solicitação de jsonp para json, ambos funcionam, basta definir o tipo de conteúdo de resposta correto

jsonp

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

json

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

Por favor, não que seu servidor não respeitará mais o SOP (política de mesma origem), mas quem se importa?

Dimitri Kopriwa
fonte
Isso não é AJAX com CORS. AJAX implica que você está usando XML. Este é JSON [P] com CORS. JSONP é "JSON" com "Padding". Se estiver enviando dados JSON, empacotados com uma chamada de função para preenchimento, então é JSONP com CORS. Você pode usar notações de dados JSON e JSONP fora de apenas injetar <script>tags em seu HTML DOM (diabos, você pode até mesmo usá-los em aplicativos de desktop, digamos que você queira fazer várias solicitações JSON para o mesmo servidor e quiser usar o nome da função como um ID de rastreamento de solicitação, por exemplo).
BrainSlugs83
-6

É possível, aqui está a minha solução:

Em seu javascript:

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

Em seu url.php:

echo "handleRequest(".$responseData.")";
nosemaj
fonte
11
Neste caso, o jQuery provavelmente transformou sua solicitação em Get de acordo com sua documentação: Nota: Isso transformará POSTs em GETs para solicitações de domínio remoto. api.jquery.com/jQuery.ajax
OneWorld