7.3 KiB
Протокол 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 как раз и призван облегчить эту задачу.
Он очень простой:
- Вместе с запросом клиент в специальном, заранее оговорённом, параметре передаёт название функции.
Обычно такой параметр называется
callback
. Например :addScript('user?id=123&*!*callback=onUserData*/!*');
- 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
возможны различные ошибки:
- Скрипт может не загрузиться: отказ в соединении, разрыв связи...
- Ошибка HTTP, например 500.
- Скрипт загрузился, но внутри некорректен и не вызывает функцию. Например, на сервере произошла ошибка и в ответе передан её текст, а вовсе не данные.
Чтобы отловить их все "одним махом", используем следующий алгоритм:
- Создаётся `