Кросс-доменные коммуникации с помощью JSONP

    Введение



    Асинхронный JavaScript и XML (AJAX) ключевая технология нового поколения сайтов, причисляемых к WEB 2.0. AJAX позволяет обрабатывать данные без назойливой перезагрузки страниц. Данные обрабатываются с использованием объекта XMLHttpREquest, который позволяет клиентскому JavaScript создавать HTTP подключения к удаленным серверам. AJAX используется во многих мешапах, которые интегрируют контент из нескольких разных ичточников.

    Однако кросс-доменные соединения запрещены – такова политика браузеров. Если вы попробуете отправить запрос на другой домен, то получите ошибку безопасности. Вы конечно можете избежать этих ошибок, если будете посылать запросы только к своему домену, но что же это будет за Web-приложение, если оно никуда дальше Вашего сервера не может сунуться. Что если Вам все-таки нужно получать данные от других доменов?



    Начальные ограничения



    Начальные ограничения заключаются в том, что скрипт загруженый с одного домена не может производить какие-либо манипуляции с другим доменом. Домен, запрашиваемого URL, должен бытаким же как и домен текущей страницы. Браузер изолирует контент из разных источников для их защиты от изменения. Эта «политика» браузеров очень стара и корнями уходит к Netscape Navigator 2.0.

    Первое, что приходит в голову для преодоления этого ограничения, это использовать страницу, которая обращается к серверу в своем домене и сервер в своем домене, который являет своеобразным прокси к необходимым сторонним серверам. Но данный подход плохо масштабируется. Другой вариант – использовать фреймы для создания на странице областей, в которые будет загружаться сторонний контент средствами GET запроса. Но после загрузки во фрейм этот контент так же станет объектом описанного выше ограничения.

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

    JSON и JSONP



    JSON – это легковесный формат (похожий на xml) дял обмена данными между браузером и сервером. JSON представляет собой текстовыю репрезентацию объектов JavaScript. Например, у Вас есть объект с двумя атрибутами: символ и цена. Можно так определить это в JavaScript.

    var ticker = {symbol: 'IBM', price: 91.42};


    А это как раз JSON представление:

    {symbol: 'IBM', price: 91.42}


    Вот пример простой JavaScript функции, которая показывает цену при вызове.

    function showPrice(data) {
        alert("Symbol: " + data.symbol + ", Price: " + data.price);
    }


    Вы можете вызывать ее используя JSON представление в качестве параметра.

    showPrice({symbol: 'IBM', price: 91.42}); // Выводит: Symbol: IBM, Price: 91.42


    А теперь вы уже готовы соединить оба этих примера в одной странице.

    function showPrice(data) {
        alert("Symbol: " + data.symbol + ", Price: " + data.price);
    }
    showPrice({symbol: 'IBM', price: 91.42});


    После загрузки страницы Вы увидите следующее сообщение:

    image

    До сих пор в этой статье я говорил только о том, как вызывать функцию со статичным JSON параметром. Номожно динамически «оборачивать» JSON в вызов функции, вы можете вызывать Ваши функции с динамическими данными – эта техника называется динамическая вставка JavaScript (dynamic JavaScript insertion). Для того чтобы понять как она работает запишем следующую строку в отдельный файл ticker.js.

    showPrice({symbol: 'IBM', price: 91.42});


    Теперь поменяем код нашей страницы:

    // Эта функция просто вызывает окно с данными из JSON
    function showPrice(data) {
        alert("Symbol: " + data.symbol + ", Price: " + data.price);
    }
    var url = “ticker.js”; // Это адрес скрипта, который мы загружаем.
    
    //А этим кодом мы динамически вставляем JavaSCript.
    var script = document.createElement('script');
    script.setAttribute('src', url);
    
    // load the script
    document.getElementsByTagName('head')[0].appendChild(script); 


    Ничего сложного. В этом римере после загрузки страницы срабатывает вторая часть скрипта, которая динамически подгружает файл ticker.js. Этот файл в свою очередь содержит только вызов функции с JSON данными.
    А теперь мы подошли к кросс-доменным коммуникациям.
    Изменим в последнем скрипте только одну строку.

    var url = "http://some_another_domain.com/script_generator.php?param1=data1¶m2=data2"; // Это адрес скрипта, который мы загружаем.


    Фактически мы можем загрузить этот скрипт откуда угодно. С любого домена. И не только загрузить, но и передать перед этим параметры методом GET. Сам же загружаемый скрипт может генерироваться любым способом. Нативная поддержка JSON есть у большинства языков.
    Share post

    Comments 22

      +2
      А собственно где JSONP? :)
        –1
        JSONP — JSON with Padding.

        Собственно это код нашего файла ticker.js

        showPrice({symbol: 'IBM', price: 91.42});
          +1
          эммм, вообще-то это несколько больше:

          в URL скрипта подставляется имя функции eg
          some_another_domain.com/script_generator.php?param1=data1¶m2=data2&callback=showPrice

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

          Смысл в том что домен чужой, класть туда свой скрипт нет возможности, поэтому ответ на запрос к нему нужно обернуть в вызов нашей функции. Имя которой и передается.

          Именно в таком виде он и поддерживается jQuery и mootools, например. Причем в jQuery достаточно добавить =? в параметры и библиотека сама обернет success call-back в публичную функцию и передаст его в этот параметр. Впрочем dataType: 'jsonp' добавляет '=?' за Вас :)

            –1
            Эмм. Вообще-то JSON — это формат данных и JSONP — тоже формат данных.

            Передача удаленному скрипту параметра callback не является обязательной или необходимой. Самое главное это, чтобы функция в которую скрипт оборачивает JSON была определена в документе.
          0
          Эх… А все-таки хотелось бы поддержки XML. Часто API разных сервисов используют именно XML, а не JSON.
            0
            Поддержки XML в чем?
              0
              code.google.com/p/crossxhr/wiki/CrossXhr

              Библиотека использует флеш, позволяет совершать кроссдоменные запросы и использовать любые типы данных. Но там тоже свои недостатки с разрешением кроссдоменных запросов для флеша.
                0
                почти все js-api которые я видел имеют формат данных json (flickr, rutube, ...)
                если нет, то нет никакой сложности разгрести строку регулярками (понятно что этот метод может возвращать и обычную строку вместо json)
                  0
                  Да. Насколько я понимаю, JSON просто легковеснее чем XML. В случае же, если нужно работать с XML, то там работает обычный DOM. Если интересно кому-то, могу написать об этом.
                0
                да, пользуюсь этим методом уже не один год=) отлично работает.
                главные недостатки:
                1) только GET запросы
                2) нет метода xhr.abort() [но можно эмулировать]
                  0
                  Можно подробнее? Для чего этот метод нужен? И как его эмулировать?
                    0
                    Для чего нужно: смотри, если пользователь будет очень часто кликать на элементах которые посылают запрос — у тебя будет образовываться очередь запросов… в которой актуален только последний запрос. То есть перед отсылкой нового запроса нужно «прибить» предыдущий, для того чтобы всегда был один единственный и последний запрос. В XMLHttpRequest() для этого служит метод abort()

                    Ну понятно, что в JSONP .abort() нет. Один из способов эмуляции — это, например, дописывать к добавляемому скрипту атрибут id. И потом смотреть — если есть элемент с таким id — то .abort() будет означать $(id).parentNode.removeChild($(id)). То есть удаляем тег скрипта с head.

                    Добавлением id к элементу script можно решить проблему с очередью — не добавлять постоянно элемент в head, а просто менять src у уже существующего тега.

                    Еще, в таком виде как ты написал, пользоваться идеей сложно. Ну представь, человек попользовался функционалом… представил свой head с пару сотнями добавленных тегов script? В общем нужно после отработки скрипта этот тег обязательно удалять. Лучше всего реализацию смотреть в jQuery.getJSON

                  0
                  Просто и доступно. Спасибо.
                  • UFO just landed and posted this here
                      0
                      С помощью xhr иы можешь получить только данные в формате JSONP. Т.е. в любом случае скрипт на стороннем домене должен отдавать данные в этом формате.
                      • UFO just landed and posted this here
                          0
                          Какого именно? Клиентского или серверного.

                          Твой клиентский JS добавляет скрипт в хедер. Если ты это сделаешь без JS то будет выглядеть вот так:

                          <script type=«text/javascript» href=«some_another_domain.com/script_generator.php?param1=data1¶m2=data2&callback=showPrice»></script>

                          script_generator.php должен вернуть данные не в формате html, а в формате jsonp.
                          Если очень нужен html, то его тоже можно засунуть в JSONP.
                          • UFO just landed and posted this here
                              0
                              <script type=«text/javascript»>showPrice({some_html: '...'});</script>
                                0
                                <script type=«text/javascript»>showPrice({some_html: '<хтмл></хтмл>});</script>
                                • UFO just landed and posted this here
                                    0
                                    Мне такие способы не известны.

                    Only users with full accounts can post comments. Log in, please.