Протокол JSONP

Протокол JSONP

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

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

Протокол JSONP – это собственно «надстройка» над таким способом взаимодействия с сервером. Давайте рассмотрим подробнее.

Протокол jsonp

Запрос

Вот пример запроса:

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>

При добавлении тега <script> с внешним атрибутом src в документ браузер тут же начинает его скачивать, а затем – выполняет.

В данном примере браузер запросит с сервера скрипт с URL /user?id=123 и выполнит.

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

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

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

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

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

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

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

Он довольно простой:

  1. Вместе с запросом клиент в специальном, заранее оговорённом, параметре передаёт название функции.Обычно такой параметр принято называть callback. Например :
    addScript('user?id=456&callback=onUserData');

Сервер кодирует данные в формат JSON и оборачивает их в вызов функции, название которой и получает из параметра callback:

// ответ сервера onUserData({ name: "Вася", age: 25 });

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

Читайте также  XMLHttpRequest метод POST

Клиентский код должен доверять серверу при вот таком запросе. Ведь серверу ничего не стоит добавить в скрипт любые команды.

Реестр CallbackRegistry

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

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

Тег может выглядеть так:

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

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

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

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

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

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

Чтобы перехватить их все сразу, используем  во такой алгоритм:

  1. Создаём <script>.
  2. На <script> вешаются  обработчики  onload/onerror (для остальных браузеров).
  3. При загрузке скрипт выполняет функцию-коллбэк CallbackRegistry…. Пусть она при запуске ставит флажок «все ок».  А мы в обработчиках проверим – если флага нет, то функция не вызывалась – стало быть, ошибка при загрузке или содержимое скрипта некорректно.

Полный пример

Итак, вот код функции, которая вызывается с url и ответом с сервера.

Он совсем небольшой:

var CallbackRegistry = {}; 
// реестр // при успехе вызовет onSuccess, при ошибке 
onError function scriptRequest(url, onSuccess, onError) { 
var scriptOk = false; // флаг, что вызов прошел успешно 
// сгенерировать имя JSONP-функции для запроса 
var callbackName = 'cb' + String(Math.random()).slice(-6); 
// укажем это имя в URL запроса 
url += ~url.indexOf('?') ? '&' : '?'; 
url += 'callback=CallbackRegistry.' + callbackName; 
// ..и создадим саму функцию в реестре 
CallbackRegistry[callbackName] = function(data) { 
scriptOk = true; // обработчик вызвался, указать что всё ок 
delete CallbackRegistry[callbackName]; // можно очистить реестр 
onSuccess(data); // и вызвать onSuccess 
}; 
// эта функция сработает при любом результате запроса // важно: при успешном результате - всегда после JSONP-обработчика 
function checkCallback() { if (scriptOk) return; 
// сработал обработчик? 
delete CallbackRegistry[callbackName]; 
onError(url); 
// нет - вызвать onError 
} 
var script = document.createElement('script'); 
// в старых IE поддерживается только событие, а не onload/onerror 
// в теории 'readyState=loaded' означает "скрипт загрузился", 
// а 'readyState=complete' -- "скрипт выполнился", но иногда 
// почему-то случается только одно из них, поэтому проверяем оба 
script.onreadystatechange = function() { 
if (this.readyState == 'complete' || this.readyState == 'loaded') { 
this.onreadystatechange = null; setTimeout(checkCallback, 0); 
// Вызвать checkCallback - после скрипта 
} } 
// события script.onload/onerror срабатывают всегда после выполнения скрипта 
script.onload = script.onerror = checkCallback; script.src = url; document.body.appendChild(script); }

Пример использования:

function ok(data) { 
alert( "Загружен пользователь " + data.name ); 
} 
function fail(url) { 
alert( 'Ошибка при запросе ' + url ); 
} // Внимание! Ответы могут приходить в любой последовательности! 
scriptRequest("user?id=123", ok, fail); // Загружен 
scriptRequest("/badurl.js", ok, fail); // fail, 404 
scriptRequest("/", ok, fail); 
// fail, 200 но некорректный скрипт

COMET

COMET через тег script реализуется при помощи длинных опросов, также как мы  уже обсуждали в уроке  COMET с XMLHttpRequest: непрерывные опросы.

Читайте также  XMLHttpRequest и возобновляемая закачка

То есть, создаётся тег <script>, браузер запрашивает скрипт у сервера и сервер оставляет соединение висеть, пока не появится, что отправить. Когда сервер хочет отправить сообщение – он отвечает, используя формат JSONP.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Поделиться

Об авторе

admin administrator

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: