пятница, 31 января 2014 г.

JSONP: Как это работает?

Достаточно интересно, я видел нескольких кандидатов на интервью за последние пару месяцев которые начинали плавать когда я задавал вопрос о использовании в JavaScript принципе одинакового источника в браузерах. Большинство из них выдавливало слово JSONP или какое нибудь другое (например прокси или CORS) но затруднялись объяснить чем одно решение отличается от другого. На самом деле, мало кто мого даже общими словами описать как работает JSONP  без привязки к JQuery, где просто нужно поставить соответсвующий флажок.

Что такое JSONP?

Если вы проведете некоторое время в размышениях о том ка JavaScript работает в браузере вы очевидно придете к осознанию того что вышеописанная политика безопасности не работает для тегов script. В том смысле что наверняка многие из нас подключали JQuery через CDN или какие то другие библиотеки из других источников. (Я думаю что подавляющее большинство).
Подобная логика и некоторые знания DOM приведут вас к подобным экспериментам с загрузкой других типов JavaScript, помимо библиотек, с помощью тегов script. Поскольку JavaScript имеет глобальную среду именования, то если вы будете ссылаться на функцию, которая может обработать ваши данные, то вы можете связать ее с вашим проложением и сделать с ней что нибудь интересное.
Роджение JSONP
Идея JSONP на самом деле очень проста: поместить в DOM тег script который ссылается на ресурс возвращающий данные JSON.  Этот сервер возвращает так называемый JSON с прокладкой (padding – отсюда и название JSON withPadding”) фуникцию, возвращаемую требуемые данные. Для того что бы вопролотить это в жизнь API сервера также должно обладать поддержкой JSONP. Как правило имя функции такое же как параметре калбэка. Например, вы хотите использовать сервис который возвращает данные JSON о погоде, которые выглядят так:

http://www.weather.com/90210

Который вернет JSON вида:

{"zipCode": "90210","location": "Beverly Hills","high": "85 degrees","low": "55 degrees"}

При добавлении поддержки JSONP, URL будет выглядеть таким образом:

http://www.weather.com/90210?callback=MyCallback

И возвращать:

MyCallback({"zipCode": "90210","location": "Beverly Hills","high": "85 degrees","low": "55 degrees"});

Эти данные теперь доступны для всего приложения, так как MyCallback – доступная и валидная функция в глобальном пространстве.
Большинство библиотечных реализации JSONP, как и в случае с JQuery будут автоматически генерировать функцию возврата и также очищать вставленный тэг script после выполнения этой функции, но базовая идея весьма и весьма проста.

Зачем использовать JSONP

JSONP настолько универсальное решение, что поддерживается практически любым браузером и существует много полезного API доступного для всех (Yahoo, Twitter, Google, etc.) Помимо обыкновенного XMLHttpRequest и разлчиных вариаций DOM, больше ничего и не нужно знать для использования JSONP в различных браузерах. В завершение, хотелось бы отметить что у большинства серьезных фреймворков (JQuery, YUI, Prototype) имеется полная поддерждка JSONP.

Когда не стоит использовать JSONP

Поднимая этот вопрос необходимо иметь представление о том как он работает. Соответственно, вы можете использовать только GET, поскольку только этот метод поддерживается тегом script. Это сразу исключает возможность использования JSONP для взаимодейтсвия с RESTful API, которые используют прочие HTTP методы, например CRUD. И поскольку мы ограничены возможностями GET, стоит учесть что мы не может использовать что нибудь еще кроме URL параметров для осуществения взаимодейтсвия с API сервера (например для передачи некоторых объектов JSON вы можете попробовать их как нибудь преобразовать к URL параметрам, но я думаю вы быстро откажетесь от этой идеи).
Во вторых, поскольку фуникцональность базируется на использовании тега  script, то не существует унифицированного способа обрабатывать ошибки. Некоторые браузеры позволяют использвать соответствующую функцию возврата  (например onError(){}), но поддержка такой функциональности существует не везде (угадайте где ее нету?) поэтому приходится использовать всевозможные хаки, или воркараунды. Если вызов API осуществяется синтаксически правильно, но вы получаете какую то ошибку, например HTTP 404, сообщающий о том что какой то ресурс не дайден , то становится очень сложно  обработать эту ситуацию и получить дополнительную информацию о том, что же все таки пошло не так. В таких случаях можно использовать соответсвующее API которое возвращает HTTP 200 но содержит дополнительную JSON информацию о произошедшей проблеме. В мире существуют проблемы и посеръезней, но таких проблемах JSONP я уже собаку съел.
И наконец, не забывайте, что JSONP является источником угрозы безопасности. Несмотря на свою невероятную полезность, она приоткрываешь брешь в политике безопасности приложения и потенциально может стать входной дверью для внешней атаки.

Какие есть альтернативы

Самой простой альтернативой является установка прокси для внешнего API через тот аддрес что и у вашего приложения. При использовании встроенных средств веб серверов, например таких как Apache Nginx, сделать это не составит труда.
Если вам не нужна поддержка IE7 и Opera, можно также использовать Cross-origin resource sharing (CORS). Это решение позволяет использовать все возможные HTTP методы для коммуникаций (включая POST объектов JSON) и обладает гораздо более высоким уровнем безопасности чем JSONP. Этот механизм имеет поддержку начиная с IE8 (правда с некоторыми оговорками) и сравнительно лекго реализуем на сервере. Недостатком является то, что многие популярные публичные JSON API, сегодня пока не поддерживают этот механизм.

Выводы

CORS >= Proxy > JSONP (в большинстве случаев).