Руководство по кроссдоменному AJAX`у

Это вольный перевод статьи Cross Domain AJAX Guide
Ни для кого не секрет, что AJAX-запросы возможны только если совпадают порты, протоколы и домены отправителя запроса и его получателя. Это означает, что следующие запросы не будут работать:

  • запрос к https://foo.bar/target.php от http://foo.bar/source.php
  • запрос к http://sub.foo.bar от http://foo.bar
  • запрос к http://foo.bar:5000 от http://foo.bar


CORS

CORS(Cross-origin resource sharing) – технология современных браузеров, которая позволяет предоставить веб-странице доступ к ресурсам другого домена(wiki). Должна поддерживаться сервером.
Если мы возьмем jQuery, запрашивающая сторона будет выглядеть следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.ajax({
    type: 'POST',
    url: 'http://d1303.de/remote/cors.php',
    crossDomain: true,
    data: "my_request_is=foo",
    dataType: 'json',
    success: function(responseData, textStatus, jqXHR)
    {
        console.log(responseData);
    },
    error: function (responseData, textStatus, errorThrown)
    {
        console.warn(responseData, textStatus, errorThrown);
        alert('CORS failed - ' + textStatus);
    }
});

Обратите внимание на crossDomain: true. Но будьте осторожны! Это будет работать только если сервер посылает соответствующие заголовки для CORS.

1
2
3
4
5
6
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
header('Access-Control-Allow-Headers: Content-Type');

echo json_encode(array("your_request_was" => $_POST['my_request_is']));

Здесь мы принимаем запросы от каждого источника для методов запроса POST, GET и OPTIONS. Для более подробной информации относительно различных параметров смотри этот документ W3C. Например, вы можете принимать запросы следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
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: POST, GET, OPTIONS');
        header('Access-Control-Max-Age: 1000');
        header('Access-Control-Allow-Headers: Content-Type');
       
        echo json_encode(array("your_request_was" => $_POST['my_request_is']));
       
    break;
}

Подробнее о серверной реализации CORS читайте на enable-cors.org.

Превосходная поддержка браузерами(IE >= 8, Firefox >= 3.5, Chrome >= 3).

JSONP

Давайте рассмотрим следующий способ реализации кроссдоменного AJAX: JSONP. Как CORS, JSONP должен поддерживаться сервером. Проще говоря, клиент называет серверу функцию обратного вызова для ответа. Сервер затем “заворачивает” ответ в эту функцию. Пример? Пример!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$.ajax({
    type: 'GET',
    url: 'http://d1303.de/remote/jsonp.php',
    data: "my_request_is=foo",
    dataType: 'jsonp',
    success: function(responseData, textStatus, jqXHR)
    {
        console.log("the response is", responseData);
    },
    error: function (responseData, textStatus, errorThrown)
    {
        console.warn(responseData, textStatus, errorThrown);
        alert('JSONP failed - ' + textStatus);
    }
});

Взгляните как выглядит запрос:

jQuery автоматически добавляет не кэшыруемый параметр с отметкой времени и – более интересно – функцию обратного вызова. На стороне сервера мы теперь можем сделать следующее:

1
2
3
4
$callback = $_GET['callback'];
$response = json_encode(array("your_request_was" => $_GET['my_request_is']));

echo $callback . "(" . $response . ")";

Теперь ответ сервера выглядит как то так:
jQuery теперь может вызвать функцию обратного вызова для “успешного” ответа. Вы также можете изменить имя обратной функции, читайте $.ajax docs

iframe

Это больше похоже на хак, чем на «чистое» решение. Этот подход заключается в том, чтобы поместить скрытый iframe в вашу страницу и затем создать скрытую форму, которая отправляется на iframe. Таким образом возможно обойти проблему кроссдоменности.
Функция:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function postIframe(target_url, method, params)
{
    //Add iframe
    var iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    iframe.style.display = "none";
   
    //Give the frame a name
    var frame_name = "frame_name" + (new Date).getTime();
    iframe.contentWindow.name = frame_name;

    //build the form
    var form = document.createElement("form");
    form.target = frame_name;
    form.action = target_url;
    form.method = method;

    //loop through all parameters
    for (var key in params)
    {
        if (params.hasOwnProperty(key))
        {
            var input = document.createElement("input");
            input.type = "hidden";
            input.name = key;
            input.value = params[key];
            form.appendChild(input);
        }
    }

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

Посмотрите, мы засунули на страницу скрытый iframe. После это мы создали форму, содержащую все необходимые нам параметры в скрытых полях. В конце мы отправили форму. Теперь вы можете увидеть такой запрос:

1
2
var obj = { my_request_is: "foo", bar: "baz" };
postIframe("http://d1303.de/remote/iframe.php", "POST", obj);

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

Другие подходы

Несмотря на то, что CORS и JSONP являются наиболее популярными методами кроссдоменного AJAXа, есть другие способы.

  • Взгляните на относительно новый window.postMessage(фича HTML5) – примеры здесь и здесь.
  • Другой классический подход, который обычно используется для такого рода проблем, заключается в расположении скрипта(например, PHP) на вашем сервера, который и будет работать с вашими AJAX-скриптами. Это не будет проблемой, потому что запрашивающая и отвечающая стороны находятся в одном домене. Смотрите отличную статью, “Создание простого API прокси-сервера на PHP“.
    comments powered by HyperComments

You may also like...

Egor Konovalov
2012-12-17 17:48:13
Есть еще один вариант - работать через Yahoo! Pipes, хотя это сильно напоминает вариант с серверным скриптом.
Ника
2015-04-29 12:20:30
жи-ши... как там в 1 классе учили...