Опубликовать данные в JsonP

102

Можно ли отправлять данные в JsonP? Или все данные должны передаваться в строке запроса как запрос GET?

У меня есть много данных, которые мне нужно отправить в службу, кросс-домен, и они слишком велики, чтобы отправлять через строку запроса

Какие есть возможности обойти это?

ChrisCa
источник

Ответы:

83

Невозможно выполнить асинхронную работу POSTс сервисом в другом домене из-за (вполне разумного) ограничения той же политики происхождения . JSON-P работает только потому, что вам разрешено вставлять <script>теги в DOM, и они могут указывать куда угодно.

Вы, конечно, можете сделать страницу в другом домене действием обычной формы POST.

Изменить : есть несколько интересных хаков , если вы готовы приложить много усилий, вставляя скрытые <iframe>s и возясь с их свойствами.

фридо
источник
Вы упомянули, что "асинхронный POST" невозможен .... тогда могу ли я выполнить синхронный POST?
Марк
4
@mark «синхронный POST» означает отправку формы, в которой используется <form method = "post" action = "http: // ... / ...">
Стивен Крыскалла
8
Это не совсем так. Вы, безусловно, можете делать POSTзапросы к другим доменам, если и этот домен, и ваш браузер поддерживают CORS. Но совершенно верно, что POSTи JSONPне совместимы.
hippietrail
2
JSONP реализуется путем вставки <script>тегов, указывающих на другой домен. Единственный способ выполнять запросы POST в браузере - использовать HTML-формы или XMLHttpRequest.
friedo
1
(в общем -) Возможно (!) выполнить асинхронный POST для службы в другом домене. ограничение на ответ. ограничение также есть на запрос JSONP.
Рой Намир
20

Если вам нужно отправить много данных кросс-домен. Обычно я создаю сервис, в который можно позвонить в два этапа:

  1. Сначала клиент отправляет ФОРМУ (отправка разрешена между доменами). Служба сохраняет входные данные в сеансе на сервере (используя GUID в качестве ключа). (клиент создает GUID и отправляет его как часть ввода)

  2. Затем клиент выполняет обычный ввод сценария (JSONP) в качестве параметра, который вы используете тот же GUID, что и в сообщении FORM. Служба обрабатывает ввод из сеанса и возвращает данные в обычном JSONP-режиме. После этого сеанс уничтожается.

Это, конечно, зависит от того, что вы пишете серверную часть.

За
источник
1
Пробовал ваш подход. Работал на FF14 и Chrome20. Opera11 и IE9 просто пост не переносили. (Проверил это с помощью своих инструментов отладки и прослушал на сервере на другом конце) Возможно, этот вопрос связан с неработоспособностью IE: stackoverflow.com/questions/10395803/… Жалоба Chrome в консоли, но все же POST: XMLHttpRequest не может load localhost: 8080 / xxx Origin null не разрешен Access-Control-Allow-Origin.
OneWorld
@OneWorld - Вы не сделали то, что было сказано в ответе. XMLHttpRequestвообще не должны быть задействованы. В ответе Per используется обычная отправка формы для выполнения запроса POST, а затем внедрение элемента сценария для выполнения запроса GET.
Quentin
7

Я знаю, что это серьезная некромантия, но я подумал, что опубликую свою реализацию JSONP POST с помощью jQuery, который я успешно использую для своего виджета JS (он используется для регистрации клиентов и входа в систему):

В основном я использую подход IFrame, как это предлагается в принятом ответе. Что я делаю иначе, так это после отправки запроса, я смотрю, можно ли получить форму в iframe с помощью таймера. Если форма недоступна, это означает, что запрос вернулся. Затем я использую обычный запрос JSONP для запроса статуса операции.

Надеюсь, что кому-то это пригодится. Протестировано в> = IE8, Chrome, FireFox и 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;
}
ВБ
источник
4

Обычно JSONP реализуется путем добавления <script>тега к вызывающему документу, так что URL-адрес службы JSONP - «src». Браузер извлекает источник сценария с помощью транзакции HTTP GET.

Теперь, если ваша служба JSONP находится в том же домене, что и ваша вызывающая страница, вы, вероятно, могли бы что-то сколотить с помощью простого $.ajax()вызова. Если это не в том же домене, то я не уверен, как это возможно.

Заостренный
источник
В данном случае это не тот домен. И я предполагаю, что возможен только GET, но хотел проверить, поскольку я только начал читать о JsonP сегодня и мне нужно принять некоторые решения о том, подходит ли он для того, что мне нужно
ChrisCa
2
Если он не находится в том же домене, но поддерживает, CORSтогда это будет возможно, если браузер также поддерживает его. В этих случаях вы будете использовать простой, JSONа не JSONP.
hippietrail
Да, @hippietrail 2 года имеет большое значение :-) CORS определенно делает это возможным, но, конечно, требует, чтобы источник данных был правильно настроен.
Pointy
0

Вы можете использовать CORS Proxy, используя этот проект . Он будет направлять весь трафик на конечную точку в вашем домене и передавать эту информацию во внешний домен. Поскольку браузер регистрирует все запросы в одном домене, мы можем публиковать JSON. ПРИМЕЧАНИЕ. Это также работает с сертификатами SSL, хранящимися на сервере.

Юджин Скрай
источник
-1

Есть (взломанное) решение, которое я делал много раз, вы сможете публиковать сообщения с помощью JsonP. (Вы сможете отправить форму, размер которой превышает 2000 символов, чем вы можете использовать с помощью GET)

Клиентское приложение 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);
  }
});

ЯВА:

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

Поступая таким образом, вы открываете свой сервер для любого почтового запроса, вы должны повторно защитить его, предоставив идентификатор или что-то еще.

С помощью этого метода вы также можете изменить тип запроса с jsonp на json, оба работают, просто установите правильный тип содержимого ответа

jsonp

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

json

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

Пожалуйста, не говорите, что ваш сервер больше не будет уважать СОП (та же политика происхождения), но кого это волнует?

Димитри Коприва
источник
Это не AJAX с CORS. AJAX подразумевает, что вы используете XML. Это JSON [P] с CORS. JSONP - это «JSON» с «заполнением». Если он отправляет данные JSON, обернутые вызовом функции для заполнения, то это JSONP с CORS. Вы можете использовать нотации данных JSON и JSONP помимо простого внедрения <script>тегов в вашу HTML DOM (черт возьми, вы даже можете использовать их в настольных приложениях, скажем, вы хотите сделать несколько запросов JSON к одному и тому же серверу и хотите использовать имя функции в качестве идентификатора отслеживания запроса, например).
BrainSlugs83
-6

Возможно, вот мое решение:

В вашем javascript:

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

В вашем url.php:

echo "handleRequest(".$responseData.")";
Nosemaj
источник
11
В этом случае jQuery, скорее всего, превратил ваш запрос в Get в соответствии с их документацией: Примечание. Это превратит POST в GET для запросов удаленного домена. api.jquery.com/jQuery.ajax
OneWorld