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

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

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

17 ответов

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

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

Вкратце, вот как вы выполняете междоменный POST от from.com/1.html до to.com/postHere.php (на примере PHP). Примечание: вам нужно только установить Access-Control-Allow-Origin для НЕ 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/". Поскольку с сервером все в порядке, браузер сделает второй запрос (на этот раз POST). Рекомендуется, чтобы ваш клиент устанавливал тип контента, который он отправляет, так что вам также нужно это разрешить.

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

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

  1. Ваш сервер должен будет обработать 2 запроса за операцию
  2. Вам придется подумать о последствиях для безопасности. Будьте осторожны, прежде чем делать что-то вроде "Access-Control-Allow-Origin: *"
  3. Это не будет работать на мобильных браузерах. По моему опыту они вообще не допускают междоменный POST. Я тестировал Android, iPad, iPhone
  4. Существует довольно большая ошибка в FF < 3.6, когда, если сервер возвращает код ответа, отличный от 400, и имеется тело ответа (например, ошибки проверки), FF 3.6 не получит тело ответа. Это огромная боль в заднице, так как вы не можете использовать хорошие практики REST. Смотрите ошибку здесь (она хранится в jQuery, но я предполагаю, что это ошибка FF - похоже, она исправлена ​​в FF4).
  5. Всегда возвращайте вышеуказанные заголовки, а не только по запросам OPTION. FF нуждается в этом в ответе от POST.

Если вы управляете удаленным сервером, вам, вероятно, следует использовать 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, точка. В противном случае это вызовет проблемы с безопасностью.

  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 был скрыт и абсолютно позиционирован. Не уверен, что браузер разрешит публикацию на нескольких сайтах, но если это так, то как это сделать.

Будь проще:

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

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

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;
}

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

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

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

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

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

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

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

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

  3. Когда все будет готово, сделайте форму submit() POST.

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

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

Если вы хотите сделать это в среде 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
            }
        });
    

Есть еще один способ (использование функции 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)
}

Высокий уровень.... Вам необходимо настроить cname на своем сервере, чтобы other-serve.your-server.com указывал на other-server.com.

Ваша страница динамически создает невидимый iframe, который служит вашим транспортом на other-server.com. Затем вы должны связаться через JS со своей страницы на other-server.com и получить обратные вызовы, которые возвращают данные обратно на вашу страницу.

Возможно, но требует координации от your-server.com и other-server.com

Это старый вопрос, но некоторые новые технологии могут помочь кому-то.

Если у вас есть административный доступ к другому серверу, вы можете использовать проект Forge с открытым исходным кодом для выполнения вашего междоменного POST. Forge предоставляет междоменную оболочку JavaScript XmlHttpRequest, которая использует API-интерфейсы необработанных сокетов Flash. POST может даже быть сделан по TLS.

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

http://github.com/digitalbazaar/forge

Я думаю, что лучший способ - это использовать XMLHttpRequest (например, $.ajax(), $.post() в jQuery) с одним из полифайлов перекрестного общего доступа к ресурсам https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills

Я знаю, что это старый вопрос, но я хотел поделиться своим подходом. Я использую cURL в качестве прокси, очень легко и последовательно. Создайте страницу php под названием submit.php и добавьте следующий код:

<?

function post($url, $data) {
$header = array("User-Agent: " . $_SERVER["HTTP_USER_AGENT"], "Content-Type: application/x-www-form-urlencoded");
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}

$url = "your cross domain request here";
$data = $_SERVER["QUERY_STRING"];
echo(post($url, $data));

Затем в вашем JS (JQuery здесь):

$.ajax({
type: 'POST',
url: 'submit.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.');
}
});

У меня есть пример кода для этой проблемы.

http://reddymails.blogspot.com/2012/05/solving-cross-domain-problem-using.html

Должно быть возможно с пользовательской таблицей YQL + JS XHR, взгляните на: http://developer.yahoo.com/yql/guide/index.html

Я использую его, чтобы выполнить некоторые операции очистки jtml на стороне клиента (js), работает нормально (у меня есть полноценный аудиоплеер, с поиском по интернету / спискам воспроизведения / текстам песен / последней информации о fm, все клиенты js + YQL)

Если у вас есть доступ к междоменному серверу и вы не хотите вносить какие-либо изменения в код на стороне сервера, вы можете использовать библиотеку под названием - 'xdomain'.

Как это устроено:

Шаг 1: сервер 1: включите библиотеку xdomain и настройте междоменный домен в качестве ведомого:

<script src="js/xdomain.min.js" slave="https://crossdomain_server/proxy.html"></script>

Шаг 2: на междоменном сервере создайте файл proxy.html и включите сервер 1 в качестве главного:

proxy.html:
<!DOCTYPE HTML>
<script src="js/xdomain.min.js"></script>
<script>
  xdomain.masters({
    "https://server1" : '*'
  });
</script>

Шаг 3:

Теперь вы можете сделать AJAX-вызов proxy.html в качестве конечной точки с сервера server1. Это обход запроса CORS. Библиотека внутренне использует решение iframe, которое работает с учетными данными и всеми возможными методами: GET, POST и т. Д.

Запросите код AJAX:

$.ajax({
        url: 'https://crossdomain_server/proxy.html',
        type: "POST",
        data: JSON.stringify(_data),
        dataType: "json",
        contentType: "application/json; charset=utf-8"
    })
    .done(_success)
    .fail(_failed)

CORS для вас. CORS - это "Обмен ресурсами между источниками", это способ отправки междоменного запроса. Теперь XMLHttpRequest2 и Fetch API поддерживают CORS и могут отправлять как запросы POST, так и GET.

Но у него есть свои ограничения. Серверу необходимо отдельно запросить Access-Control-Allow-Origin, и его нельзя установить на "*".

И если вы хотите, чтобы любой источник мог отправить вам запрос, вам нужен JSONP (также необходимо установить Access-Control-Allow-Origin, но может быть '*')

Для большого количества запросов, если вы не знаете, как сделать выбор, я думаю, что вам нужен полнофункциональный компонент, чтобы сделать это. Позвольте мне представить простой компонент https://github.com/Joker-Jelly/catta


Если вы используете современный браузер (> IE9, Chrome, FF, Edge и т. Д.), Очень рекомендую использовать простой, но красивый компонент https://github.com/Joker-Jelly/catta. У него нет зависимости, меньше чем 3 КБ, и он поддерживает Fetch, AJAX и JSONP с тем же синтаксисом и опциями смертельного образца.

catta('./data/simple.json').then(function (res) {
  console.log(res);
});

Он также поддерживает импорт в ваш проект, например, модуль ES6, CommonJS и даже <script> в HTML.

Другие вопросы по тегам