Как отправить междоменный POST-запрос через JavaScript? - PullRequest
540 голосов
/ 18 ноября 2008

Как отправить междоменный запрос POST через JavaScript?

Примечания - это не должно обновлять страницу, и мне нужно впоследствии получить и проанализировать ответ.

Ответы [ 17 ]

374 голосов
/ 30 сентября 2011

Обновление: Прежде чем продолжить, все должны прочитать и понять учебник html5rocks по CORS. Это легко понять и очень ясно.

Если вы управляете сервером POST, просто используйте «Стандарт общего доступа к ресурсам», установив заголовки ответов на сервере. Этот ответ обсуждается в других ответах в этой теме, но, на мой взгляд, не очень ясно.

Вкратце, вот как вы выполняете междоменную POST от from.com/1.html до to.com/postHere.php (используя PHP в качестве примера). Примечание: вам нужно только установить Access-Control-Allow-Origin для запросов NON OPTIONS - этот пример всегда устанавливает все заголовки для меньшего фрагмента кода.

  1. В postHere.php установите следующее:

    switch ($_SERVER['HTTP_ORIGIN']) {
        case 'http://from.com': case 'https://from.com':
        header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
        header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
        header('Access-Control-Max-Age: 1000');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
        break;
    }
    

    Это позволяет вашему скрипту создавать междоменные POST, GET и OPTIONS. Это станет ясно, когда вы продолжите читать ...

  2. Настройте свой междоменный POST из JS (пример jQuery):

    $.ajax({
        type: 'POST',
        url: 'https://to.com/postHere.php',
        crossDomain: true,
        data: '{"some":"json"}',
        dataType: 'json',
        success: function(responseData, textStatus, jqXHR) {
            var value = responseData.someKey;
        },
        error: function (responseData, textStatus, errorThrown) {
            alert('POST failed.');
        }
    });
    

Когда вы выполните POST на шаге 2, ваш браузер отправит на сервер метод «OPTIONS». Это «фыркание» браузером, чтобы увидеть, крут ли сервер, когда вы размещаете на нем сообщение. Сервер отвечает «Access-Control-Allow-Origin», сообщая браузеру, что он в порядке: «POST | GET | ORIGIN», если запрос возник из «http://from.com" или» https://from.com"., так как сервер в порядке с это, браузер сделает 2-й запрос (на этот раз POST). Рекомендуется, чтобы ваш клиент устанавливал тип контента, который он отправляет, поэтому вам также необходимо это разрешить.

MDN имеет отличную статью о управлении доступом HTTP , в которой подробно описывается, как работает весь поток. Согласно их документам, он должен «работать в браузерах, которые поддерживают межсайтовый XMLHttpRequest». Это немного вводит в заблуждение, так как я THINK только современные браузеры допускают междоменный POST. Я проверял только это работает с Safari, Chrome, FF 3.6.

Имейте в виду следующее, если вы делаете это:

  1. Вашему серверу придется обрабатывать 2 запроса на операцию
  2. Вам придется подумать о последствиях для безопасности. Будьте осторожны, прежде чем делать что-то вроде «Access-Control-Allow-Origin: *»
  3. Это не будет работать в мобильных браузерах. По моему опыту они вообще не допускают междоменный POST. Я тестировал Android, iPad, iPhone
  4. В FF <3.6 существует довольно большая ошибка, когда, если сервер возвращает код ответа, отличный от 400, и существует тело ответа (например, ошибки проверки), FF 3.6 не получит тело ответа. Это огромная боль в заднице, так как вы не можете использовать хорошие практики REST. См. Ошибку <a href="http://bugs.jquery.com/ticket/7868#comment:11"> здесь (она хранится в jQuery, но я предполагаю, что это ошибка FF - похоже, она исправлена ​​в FF4).
  5. Всегда возвращайте вышеуказанные заголовки, а не только по запросам OPTION. FF это нужно в ответе от POST.
118 голосов
/ 29 мая 2011

Если вы управляете удаленным сервером, вам, вероятно, следует использовать CORS, как описано в этом ответе ; он поддерживается в IE8 и более поздних версиях, а также во всех последних версиях FF, GC и Safari. (Но в IE8 и 9 CORS не позволит вам отправлять куки в запросе.)

Итак, если вы не управляете удаленным сервером, или если вам нужно поддерживать IE7, или если вам нужны файлы cookie, и вы должны поддерживать IE8 / 9, вы, вероятно, захотите использовать техника iframe.

  1. Создать фрейм с уникальным именем. (iframes используют глобальное пространство имен для всего браузера, поэтому выберите имя, которое не будет использовать ни один другой сайт.)
  2. Создайте форму со скрытыми входами, нацеливаясь на iframe.
  3. Отправьте форму.

Вот пример кода; Я тестировал его на IE6, IE7, IE8, IE9, FF4, GC11, S5.

function crossDomainPost() {
  // Add the iframe with a unique name
  var iframe = document.createElement("iframe");
  var uniqueString = "CHANGE_THIS_TO_SOME_UNIQUE_STRING";
  document.body.appendChild(iframe);
  iframe.style.display = "none";
  iframe.contentWindow.name = uniqueString;

  // construct a form with hidden inputs, targeting the iframe
  var form = document.createElement("form");
  form.target = uniqueString;
  form.action = "http://INSERT_YOUR_URL_HERE";
  form.method = "POST";

  // repeat for each parameter
  var input = document.createElement("input");
  input.type = "hidden";
  input.name = "INSERT_YOUR_PARAMETER_NAME_HERE";
  input.value = "INSERT_YOUR_PARAMETER_VALUE_HERE";
  form.appendChild(input);

  document.body.appendChild(form);
  form.submit();
}

Осторожно! Вы не сможете напрямую прочитать ответ POST, поскольку iframe существует в отдельном домене. Кадры не могут общаться друг с другом из разных доменов; это политика того же происхождения .

Если вы управляете удаленным сервером, но не можете использовать CORS (например, потому что вы используете IE8 / IE9 и вам нужно использовать куки), есть способы обойти политику одного источника, например, с помощью window.postMessage и / или одна из нескольких библиотек, позволяющих отправлять междоменные кросс-фреймовые сообщения в старых браузерах:

Если вы не управляете удаленным сервером, вы не можете прочитать ответ POST, точка. В противном случае это вызовет проблемы с безопасностью.

46 голосов
/ 18 ноября 2008
  1. Создать iFrame,
  2. положить форму с скрытыми вводами,
  3. установить действие формы для URL,
  4. Добавить iframe в документ
  5. отправить форму

псевдокод

 var ifr = document.createElement('iframe');
 var frm = document.createElement('form');
 frm.setAttribute("action", "yoururl");
 frm.setAttribute("method", "post");

 // create hidden inputs, add them
 // not shown, but similar (create, setAttribute, appendChild)

 ifr.appendChild(frm);
 document.body.appendChild(ifr);
 frm.submit();

Вы, вероятно, хотите стилизовать iframe, быть скрытым и абсолютно позиционированным. Не уверен, что межузловая публикация будет разрешена браузером, но если это так, то как это сделать.

22 голосов
/ 03 октября 2014

Не усложняйте:

  1. междоменный POST:
    используйте crossDomain: true,

  2. не должен обновлять страницу:
    Нет, он не обновит страницу, поскольку асинхронный обратный вызов success или error будет вызван, когда сервер отправит ответ обратно.


Пример сценария:

$.ajax({
        type: "POST",
        url: "http://www.yoururl.com/",
        crossDomain: true,
        data: 'param1=value1&param2=value2',
        success: function (data) {
            // do something with server response data
        },
        error: function (err) {
            // handle your error logic here
        }
    });
15 голосов
/ 22 января 2011

Если у вас есть доступ ко всем задействованным серверам, укажите в заголовке ответа следующую страницу для запрашиваемой страницы в другом домене:

PHP:

header('Access-Control-Allow-Origin: *');

Например, в коде Drupal xmlrpc.php вы должны сделать это:

function xmlrpc_server_output($xml) {
    $xml = '<?xml version="1.0"?>'."\n". $xml;
    header('Connection: close');
    header('Content-Length: '. strlen($xml));
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/x-www-form-urlencoded');
    header('Date: '. date('r'));
    // $xml = str_replace("\n", " ", $xml); 

    echo $xml;
    exit;
}

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

9 голосов
/ 25 февраля 2010

Проверьте функцию post_method в http://taiyolab.com/mbtweet/scripts/twitterapi_call.js - хороший пример описанного выше метода iframe.

6 голосов
/ 25 января 2012

Еще одна важная вещь, которую стоит отметить !!! В примере выше описано, как использовать

$.ajax({
    type     : 'POST',
    dataType : 'json', 
    url      : 'another-remote-server',
    ...
});

JQuery 1.6 и ниже имеет ошибку с междоменным XHR. Согласно Firebug, никаких запросов, кроме ВАРИАНТОВ, не было отправлено. Нет пост. На всех.

Потратил 5 часов на тестирование / настройку моего кода. Добавление большого количества заголовков на удаленный сервер (скрипт). Без какого-либо эффекта. Но позже я обновил JQuery lib до 1.6.4, и все работает как шарм.

6 голосов
/ 09 марта 2010
  1. Создайте два скрытых фрейма (добавьте «display: none;» в стиль css). Сделайте так, чтобы ваш второй iframe указывал на что-то в вашем собственном домене.

  2. Создайте скрытую форму, установите для ее метода «post» с target = вашим первым iframe и, при желании, установите для enctype «multipart / form-data» (я думаю, вы хотите сделать POST, потому что отправить данные из нескольких частей, например изображения?)

  3. Когда все будет готово, заполните форму отправки () POST.

  4. Если вы можете заставить другой домен возвращать javascript, который будет выполнять междоменную связь с фреймами (http://softwareas.com/cross-domain-communication-with-iframes)), то вам повезло, и вы также можете получить ответ.

Конечно, если вы хотите использовать свой сервер в качестве прокси, вы можете избежать всего этого. Просто отправьте форму на свой собственный сервер, который перенаправит запрос на другой сервер (при условии, что другой сервер не настроен на обнаружение несоответствия IP-адресов), получите ответ и верните все, что захотите.

5 голосов
/ 06 мая 2014

Если вы хотите сделать это в среде ASP.net MVC с JQuery AJAX, выполните следующие действия: (это краткое изложение решения, предложенного на этой ветке)

Предположим, что "caller.com" (может быть любым веб-сайтом) необходимо опубликовать на "server.com" (приложение ASP.net MVC)

  1. В Web.config приложения "server.com" добавьте следующий раздел:

      <httpProtocol>
          <customHeaders>
              <add name="Access-Control-Allow-Origin" value="*" />
              <add name="Access-Control-Allow-Headers" value="Content-Type" />
              <add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS" />
          </customHeaders>
      </httpProtocol>
    
  2. На "server.com" у нас будет следующее действие на контроллере (называемом "Home"), на котором мы будем публиковать сообщения:

    [HttpPost]
    public JsonResult Save()
    {
        //Handle the post data...
    
        return Json(
            new
            {
                IsSuccess = true
            });
    }
    
  3. Затем из "caller.com" отправьте данные из формы (с html id "formId") на "server.com" следующим образом:

    $.ajax({
            type: "POST",
            url: "http://www.server.com/home/save",
            dataType: 'json',
            crossDomain: true,
            data: $(formId).serialize(),
            success: function (jsonResult) {
               //do what ever with the reply
            },
            error: function (jqXHR, textStatus) {
                //handle error
            }
        });
    
3 голосов
/ 28 июня 2014

Есть еще один способ (использование функции html5). Вы можете использовать прокси-сервер iframe, размещенный в этом другом домене, вы отправляете сообщение, используя postMessage, в этот iframe, тогда этот iframe может выполнить запрос POST (в том же домене) и postMessage обратно с reposnse в родительское окно.

родитель на sender.com

var win = $('iframe')[0].contentWindow

function get(event) {
    if (event.origin === "http://reciver.com") {
        // event.data is response from POST
    }
}

if (window.addEventListener){
    addEventListener("message", get, false)
} else {
    attachEvent("onmessage", get)
}
win.postMessage(JSON.stringify({url: "URL", data: {}}),"http://reciver.com");

iframe на reciver.com

function listener(event) {
    if (event.origin === "http://sender.com") {
        var data = JSON.parse(event.data);
        $.post(data.url, data.data, function(reponse) {
            window.parent.postMessage(reponse, "*");
        });
    }
}
// don't know if we can use jQuery here
if (window.addEventListener){
    addEventListener("message", listener, false)
} else {
    attachEvent("onmessage", listener)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...