Як надіслати міждоменний POST-запит через JavaScript?


568

Як надіслати міждоменний POST-запит через JavaScript?

Примітки - це не повинно оновлювати сторінку, і мені потрібно потім схопити та проаналізувати відповідь.


Мені хотілося б трохи дізнатися про випадок використання, який дозволяє вам спробувати це зробити. Скажіть, будь ласка, щось про це?
mkoeller

В основному я працюю над сценарієм, який повинен відправити текст з HTML-файлу на інший сервер для обробки.
Ідо Шачам

3
Чи можете ви встановити проксі-сервер, який робить це на стороні сервера і просто дає результат вашому сценарію? Або це повинен бути 100% JavaScript?
Саша Чедигов

Відповіді:


382

Оновлення: Перш ніж продовжувати, кожен повинен прочитати та зрозуміти підручник html5rocks на CORS. Це легко зрозуміти і дуже зрозуміло.

Якщо ви керуєте тим, що сервер буде POSTed, просто скористайтеся "стандартом перехресного походження ресурсів", встановивши заголовки відповідей на сервері. Ця відповідь обговорюється в інших відповідях у цій темі, але не дуже чітко, на мою думку.

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

  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", повідомляючи веб-переглядачу про OK для 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.

Чи можна повернути HTML, наприклад? Мені потрібно повернути html, а щось не працює ...
denis_n

Так, ви повинні вміти. Ніколи не пробував це тхо. Ваш сервер повертає 200? Також ваш сервер повертає заголовки за запитами OPTIONS AND POST? Я оновив свою відповідь більш детально про це. Переконайтеся, що ваш сервер також відповідає правильним заголовком типу вмісту (наприклад, текст / html). Моя рекомендація полягає у використанні Google Chrome, правою кнопкою миші та оглядом елемента. Перейдіть на вкладку мережі та дивіться POST та відповідь. Повинен дати вам інформацію про те, що йде не так.
rynop

Я спробував це, але все ж отримую 400 Bad Requestза OPTIONSзапитом. а у firefoxдругому запиті POSTніколи не робиться. :(
Заїн Шейх

Чи є спосіб висловити свою локальну машину у вашій заяві справи вище? Або вам просто потрібно використовувати * у цьому випадку для джерел дозволу.
Тодд Ванс

1
це востаннє було відредаговано 4 роки тому - чи буде це працювати в мобільних браузерах зараз?
франкінто

121

Якщо ви керуєте віддаленим сервером, ви, ймовірно, повинні використовувати CORS, як описано в цій відповіді ; він підтримується в IE8 і новіших версіях, а також у всіх останніх версіях FF, GC та Safari. (Але в IE8 та 9, CORS не дозволить вам надсилати файли cookie у запиті.)

Отже, якщо ви не керуєте віддаленим сервером, або якщо у вас є підтримка 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 і вам потрібно використовувати файли cookie), існують способи подолати політику одного і того ж джерела, наприклад, використовуючи window.postMessageта / або одна з ряду бібліотек, що дозволяє надсилати міждоменні міжкадрові повідомлення у старих браузерах:

Якщо ви не керуєте віддаленим сервером, ви не можете прочитати відповідь POST, періоду. Інакше це може спричинити проблеми із безпекою.


2
Вам потрібно буде щось встановити form.target, інакше браузер перейде від вашого веб-сайту до URL-адреси дії форми. Крім того, струна повинна бути унікальною; якщо є інші кадри чи вікна, що використовують те саме ім'я, форма може розміщувати повідомлення у цьому вікні замість вашого iframe. Але наскільки вона унікальна? Напевно, не дуже. Шанси клобінгу досить невеликі.
знизати

1
@Nawaz Як я вже говорив у своїй відповіді, для отримання результату на своїй веб-сторінці вам доведеться здійснювати міждоменне міжкадрове спілкування. Це вимагає, щоб ви керували віддаленим веб-сервером, щоб ви могли змінити його відповідь, щоб дозволити зв’язок із вашою веб-сторінкою. (По-перше, сервер повинен відповісти HTML-
кодом

1
+1 - це найкраще рішення, яке я знайшов, якщо у вас немає доступу до сервера
Джеймс Лонг

1
@VojtechB Ні, це була б дірка для безпеки.
Дан Фабуліч

1
@Andrus Ви можете прочитати результат POST, але тільки якщо ви керуєте сервером! Див. У цій відповіді написано: "зробіть X на відправника [клієнта], зробіть Y на приймачі [сервері]". Якщо ви не керуєте приймачем / сервером, ви не можете робити Y, і тому ви не можете прочитати результат POST.
Дан Фабуліч

48
  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();

Ймовірно, ви хочете, щоб стиль рамки був прихованим і абсолютно розміщеним. Не впевнений, що веб-сайт розміщуватиме веб-переглядач, але якщо це так, це зробити.


4
Насправді це трохи неточно, оскільки ifr.appendChild (frm); не вийде. iframe - це посилання на об'єкт вікна, а метод appendChild для нього не існує. Вам потрібно спершу схопити вузол документа в iframe. Для роботи браузера потрібне виявлення функцій.
Ракеш Пай

Дякую. Знайшли ці корисні посилання стосовно цього питання: bindzus.wordpress.com/2007/12/24/… developer.apple.com/internet/webcontent/iframe.html
Ido Schacham

19
Проблема! Отримана відповідь у iframe лежить на іншому домені, тому головне вікно не має доступу до нього, а також iframe не має доступу до головного вікна. Тож це рішення здається гарним лише для виконання пошти, але ви не зможете згодом проаналізувати відповідь :(
Ido Schacham

2
Спробуйте встановити завантаження в тезі тіла відповіді на функцію JavaScript, яка викликає функцію у батьків з рядком відповіді.
Лу Франко

Ця відповідь не спрацювала для мене; Я розмістив свою власну варіацію нижче.
Дан Фабуліч

24

Не ускладнювати:

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

8
crossDomain: trueдивно не має нічого спільного з реальними запитами між домену. Якщо запит є міждоменним, jquery встановлює це істинно автоматично.
Кевін Б

16

Якщо у вас є доступ до всіх задіяних серверів, введіть у заголовку відповіді наступну сторінку, яку вимагають в іншому домені:

PHP:

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

Наприклад, у xmlrpc.php-коді Drupal ви зробите це:

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

Це, ймовірно, створює проблеми із безпекою, і ви повинні переконатися, що вживаєте відповідних заходів для підтвердження запиту.



6
  1. Створіть два прихованих кадрів (додайте "display: none;" в стиль css). Зробіть свою другу рамку iframe на щось у власному домені.

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

  3. Коли будете готові, змусьте форму подати () пошту.

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

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


6

Ще одне важливе, що слід зазначити !!! У прикладі вище описано, як користуватися

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

JQuery 1.6 і новіші версії має помилку з крос-доменом XHR. За повідомленням Firebug, жодних запитів, крім OPTIONS, не надсилалося. Немає посту. Зовсім.

Провів 5 годин тестування / налаштування свого коду. Додавання безлічі заголовків на віддалений сервер (скрипт). Без жодного ефекту. Але пізніше я оновив JQuery lib до 1.6.4, і все працює як шарм.


Whoopps, не в Opera 10.61. Моє остаточне рішення зробити це - використовувати PHP-проксі в моєму домені.
BasTaller

Як ви використовували проксі PHP? Чи можете ви мене на цьому направляти?
Zoran777

дивіться відповіді нижче, наприклад, Іван Дерст
BasTaller

5

Якщо ви хочете зробити це в середовищі 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" ми виконаємо наступну дію щодо контролера (званий "Домашня сторінка"), до якого ми будемо публікувати:

    [HttpPost]
    public JsonResult Save()
    {
        //Handle the post data...
    
        return Json(
            new
            {
                IsSuccess = true
            });
    }
  3. Потім з "caller.com" розміщуйте дані з форми (з ідентифікатором html "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
            }
        });

4

Є ще один спосіб (за допомогою функції html5). Ви можете використовувати iframe проксі, розміщений на цьому іншому домені, ви надсилаєте повідомлення за допомогою postMessage до цього iframe, тоді цей iframe може робити запит POST (на тому ж домені) і назад postMessage з відкликом до батьківського вікна.

батьків на 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)
}

Є відповідне запитання в stackoverflow.com/questions/38940932/… . Чи можливо створити якийсь плагін або загальну функцію на основі вашої вибірки?
Андрус

@Andrus, можливо, щось на кшталт цього gist.github.com/jcubic/26f806800abae0db9a0dfccd88cf6f3c
jcubic

Цей код вимагає змінити сторінку приймача. Як прочитати відповідь, якщо сторінки приймача не вдається змінити?
Андрус

@Andrus вам не потрібно мати доступ до iframe recever.com для надсилання туди ajax. Без iframe запитів не буде.
jcubic

3

Високий рівень .... Вам потрібно встановити ім’я на вашому сервері, щоб other-serve.your-server.com вказував на other-server.com.

Ваша сторінка динамічно створює невидимий кадр, який виступає вашим транспортом до other-server.com. Тоді вам доведеться спілкуватися через JS зі своєї сторінки на other-server.com і мати зворотні дзвінки, які повертають дані на вашу сторінку.

Можлива, але вимагає координації з вашого веб-сайту www.server.com та інших-server.com


Навіть не думав використовувати CNAME для переадресації. Гарний дзвінок! Я ще повинен спробувати це, але я припускаю, що CNAME надумає браузера думати, що він взаємодіє з тим самим сайтом? Я буду використовувати його для публікації на Amazon S3, тому сподіваюся, що це працює.
SpencerElliott

1
Я не бачу, як це все вирішить. перехід до іншого субдомену має ті самі проблеми, що і перехід на інший домен.
Восьминіг


2

Це давнє запитання, але якась нова технологія може комусь допомогти.

Якщо у вас є адміністративний доступ до іншого сервера, тоді ви можете скористатися проектом Forge Open Source для виконання вашого міждоменного POST. Forge надає міждоменну обгортку JavaScript XmlHttpRequest, яка використовує перевагу API для необробленого сокета Flash. POST може бути виконано навіть через TLS.

Причина, що вам потрібен адміністративний доступ до сервера, на який ви відправляєте повідомлення, полягає в тому, що ви повинні надати міждоменну політику, яка дозволяє доступ з вашого домену.

http://github.com/digitalbazaar/forge


2

Я знаю, що це старе питання, але я хотів поділитися своїм підходом. Я використовую 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.');
}
});

1

Якщо це можливо за допомогою спеціальної таблиці YQL + JS XHR, подивіться на сторінку: http://developer.yahoo.com/yql/guide/index.html

Я використовую його для того, щоб зробити деякий розмір html-файлів у форматі HTML (js), добре працює (у мене є повний аудіоплеєр, з пошуком в Інтернеті / списках відтворення / текстами пісень / останньою інформацією fm, всім клієнтом js + YQL)


1

CORS - це для вас. CORS - "Перехресний розподіл ресурсів", це спосіб надіслати запит між доменом. Тепер XMLHttpRequest2 та 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.


1

Якщо у вас є доступ до міждоменного сервера і ви не хочете вносити жодних змін коду на стороні сервера, ви можете використовувати бібліотеку під назвою «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 як кінцеву точку з сервера1. Це обхід запиту 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.