en.javascript.info/4-ajax/10-ajax-jsonp/article.md
2015-02-27 13:21:58 +03:00

7.3 KiB
Raw Blame History

Протокол JSONP

Если создать тег <script src>, то при добавлении в документ запустится процесс загрузки src. В ответ сервер может прислать скрипт, содержащий нужные данные.

Таким образом можно запрашивать данные с любого сервера, в любом браузере, без каких-либо разрешений и дополнительных проверок.

Протокол JSONP -- это "надстройка" над таким способом коммуникации. Здесь мы рассмотрим его использование в деталях.

[cut]

Запрос

Простейший пример запроса:

function addScript(src) {
  var elem = document.createElement("script");
  elem.src = src;
  document.head.appendChild(elem);
}

addScript('user?id=123');

Такой вызов добавит в HEAD документа тег:

<script src="/user?id=123"></script>

Браузер тут же обработает его: запросит /user?id=123 с заданного URL и выполнит.

Обработка ответа, JSONP

В примере выше рассмотрено создание запроса, но как получить ответ? Допустим, сервер хочет прислать объект с данными.

Конечно, он может присвоить её в переменную, например так:

// ответ сервера
var user = {name: "Вася", age: 25 };

...А браузер по script.onload отловит окончание загрузки и прочитает значение user.

Но что, если одновременно делается несколько запросов? Получается, нужно присваивать в разные переменные.

Протокол JSONP как раз и призван облегчить эту задачу.

Он очень простой:

  1. Вместе с запросом клиент в специальном, заранее оговорённом, параметре передаёт название функции.

    Обычно такой параметр называется callback. Например :

    addScript('user?id=123&*!*callback=onUserData*/!*');
    
  2. Cервер кодирует данные в JSON и оборачивает их в функцию, название которой получает из параметра `callback`:
    // ответ сервера
    onUserData({
      name: "Вася",
      age: 25
    });
    

Это и называется JSONP ("JSON with Padding").

[warn header="Аспект безопасности"] Клиентский код должен доверять серверу при таком запросе. Ведь серверу ничего не стоит добавить в скрипт любые команды. [/warn]

Реестр CallbackRegistry

В примере выше функция onUserData должна быть глобальной, ведь <script src> выполняется в глобальной области видимости.

Хотелось бы не загрязнять глобальное пространство имён, или по крайней мере свести загрязнение к минимуму.

Как правило, для этого создают один глобальный объект "реестр", который мы назовём CallbackRegistry. Далее для каждого запроса в нём генерируется временная функция.

Тег будет выглядеть так:

<script src="user?id=123&callback=*!*CallbackRegistry.func12345*/!*"></script>

Сервер обернёт ответ в функцию CallbackRegistry.func12345, она вызывает нужный обработчик и очищает память, удаляя себя.

Далее мы посмотрим более полный код всего этого, но перед этим -- важный момент! Нужно предусмотреть обработку ошибок.

Обнаружение ошибок

При запросе данных при помощи SCRIPT возможны различные ошибки:

  1. Скрипт может не загрузиться: отказ в соединении, разрыв связи...
  2. Ошибка HTTP, например 500.
  3. Скрипт загрузился, но внутри некорректен и не вызывает функцию. Например, на сервере произошла ошибка и в ответе передан её текст, а вовсе не данные.

Чтобы отловить их все "одним махом", используем следующий алгоритм:

  1. Создаётся `