Автоматизация использования Javascript API Вконтакте

    Пару дней назад, как обычно, никого не поставив в известность, Контакт добавил возможность разработки приложений на JavaScript, подключаемых через iframe. Доступ к API в этих приложениях, в общем-то, такой же, как и доступ к API из Flash — так или иначе, это обычный REST-протокол. Параметры передаются через строку запроса GET, а результат возвращается в виде JSON или XML.

    Одной из проблем использования API Вконтакте является передача кроссдоменного запроса на http://api.vkontakte.ru/api.php. Другая проблема — громоздкое вычисление подписи, которой необходимо подписывать все запросы.Первым делом, я, как и многие другие разработчики, взялся за jQuery. Однако, во-первых, использовать 68-килобайтную библиотеку ради всего лишь кроссдоменных запросов — это как стрелять из пушки по воробьям. Да и как-то громоздко получился вызов API-функций с использованием jQuery, так что пришлось от jQuery отказаться в пользу маленькой функции для кроссдоменных запросов. Ну и чтобы как-то упростить себе жизнь, я решил сделать небольшую библиотеку, которая бы не только загружала указанный URL с получением ответа сервера, но и выполняла бы самое неудобное во всем этом API — вычисление подписи.

    С конечным результатом можно ознакомиться, перейдя по ссылкам в конце статьи, а я остановлюсь на отдельных моментах использования и реализации.

    Для использования API Вконтакте, к HTML-документу приложения необходимо, для начала, подключить файл с библиотекой:
    1. <!-- В данном случае, скрипт расположен в каталоге 
    2.      с соответствующим HTML-файлом --> 
    3. <script src="vk_api.js" type="text/javascript"></script> 
    * This source code was highlighted with Source Code Highlighter.
    Сама библиотека представляет собой один объект vk_api и функцию md5, честно позаимствованную мной из Webtoolkit. Объект vk_api сам по себе к использованию не готов — от него необходимо создать экземпляр при помощи конструктора:
    1. var api = new vk_api(/* параметры */);
    * This source code was highlighted with Source Code Highlighter.
    В принципе, можно было бы выполнить всю инициализацию при загрузке самого скрипта vk_api.js, но это не путь самурая — а вдруг вызывать API в этом сеансе вообще не придется? К тому же, при инициализации конструктору передаются параметры, которые так или иначе необходимо где-то устанавливать и передавать скрипту.

    Итак, попробуем создать объект:
    1. //Создаем объект от vk_api(). Вся дальнейшая работа должна проходить 
    2. //только с объектом api. 
    3. var api = new vk_api( 
    4.     //Это Секрет, он указан на странице редактирования приложения 
    5.     'hGtsH5G0'
    6.  
    7.     //Эта функция будет вызвана, если инициализация API пройдет успешно. 
    8.     //Хорошее место для установки всех callback-функций через 
    9.     //addCallback() 
    10.     function() { 
    11.  
    12.         //Вызываем окно изменения настроек с отмеченными настройками для 
    13.         //доступа к друзьям и к стене. Пользователь не сможет закрыть 
    14.         //вызванное окно, пока не установит требуемые настройки. 
    15.         api.makeSettings(api.SETT_FRIENDS | api.SETT_WALL); 
    16.  
    17.         //Добавляем функцию, которая запустится, если пользователь добавит 
    18.         //приложение к себе на страницу. 
    19.         api.addCallback( 
    20.           'onApplicationAdded'
    21.             function() { 
    22.                 //действия 
    23.             } 
    24.         ); 
    25.     }, 
    26.  
    27.     //Эта функция вызывается, если инициализация API не состоялась. 
    28.     //В этом случае, невозможно использовать внешние (external) вызовы. 
    29.     function() { 
    30.         //сообщение об ошибке 
    31.     }
    32. );
    * This source code was highlighted with Source Code Highlighter.
    Как видите, все очень просто. Конструктору передается якобы секретный ключ (ага, секретный — в открытом виде представлен в коде страницы) и две функции, первая из которых выполняется в случае успешной инициализации, а другая — в случае возникновения ошибки при инициализации. На самом деле, вторая функция вовсе необязательна, если разработчик не хочет учитывать те единичные случаи, когда произойдет ошибка при инициализации. Кроме того, есть еще один параметр, который представляет собой обычный логический флаг, говорящий о том, включен ли режим тестирования.

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

    Если посмотреть пару тройку десятков приложений Вконтакте, то очень многие из них прямо таки требуют, чтобы пользователи их установили и разрешили им какие-то действия. Приложения в этом случае либо просто выводят сообщение с инструкцией о том, куда мне идти что надо сделать, либо, в лучшем случае, сразу отображают окно установки или изменения настроек. Ну грех было не реализовать подобные функции на уровне библиотеки. Причем, по просьбе одного человека, я ушел немного дальше, и реализовал указанные функции таким образом, что пользователь вообще не сможет закрыть окошко с предложением установить приложение или что-то ему разрешить, пока не согласится с требованиями приложения. Таким образом не надо предусматривать дополнительные страницы приложения для тех пользователей, которые отказались от установки или что-то приложению не разрешили — достаточно просто вызвать одну или две функции:
    1. //Выводим окно с просьбой установить приложение
    2. api.makeInstall();
    3. api.addCallback(
    4.     'onApplicationAdded',
    5.  
    6.     //Эта функция будет вызвана, когда пользователь установит приложение
    7.     function() {
    8.         $('#text').append('Application added\n');
    9.         
    10.         //Просим у пользователя разрешить приложению доступ к
    11.         //друзьям и к публикации на стенах
    12.         api.makeSettings(api.SETT_FRIENDS | api.SETT_WALL);
    13.         api.addCallback(
    14.          'onSettingsChanged',
    15.  
    16.             //Функция выполнится, когда пользователь изменит настройки приложения
    17.             function(settings) {
    18.                 $('#text').append('New parameters: ' + String(settings) + '\n');
    19.             }
    20.         );
    21.     }
    22. );
    * This source code was highlighted with Source Code Highlighter.
    Перейдем, наконец, к тому, ради чего приложение и разрабатывалось, т. е. непосредственно к вызову API-функций. Для этого предусмотрен метод call:
    1. //Вызываем метод API через функцию call() 
    2. api.call( 
    3.  
    4.     //Название метода API. 
    5.     //Допустим, получение расширенной информации о профилях. 
    6.     'getProfiles'
    7.  
    8.     //Это объект с параметрами метода API. Если параметров нет, 
    9.     //то этот аргумент можно не указывать. 
    10.     { 
    11.         uids: '4124584,2327412,1237975'
    12.  
    13.         //Можно указывать массивы, которые будут автоматически 
    14.         //преобразованы в строки. 
    15.         fields: [ 
    16.             'first_name', 'last_name', 'nickname'
    17.             'bdate', 'city' , 'country', 'photo' 
    18.         ] 
    19.     }, 
    20.  
    21.     //Эта функция будет вызвана, когда будет получен ответ от сервера. 
    22.     //data - это готовый к использованию объект, созданный из строки 
    23.     //JSON, полученной от сервера. 
    24.     function(data) { 
    25.         //Действия с объектом data 
    26.     } 
    27. );
    * This source code was highlighted with Source Code Highlighter.
    Как видите, все просто, и никаких лишних действий, что нам и нужно.

    Что касается так называемого секретного ключа, передаваемого конструктору vk_api первым параметром, то для него я предусмотрел простейшее, даже не шифрование, а кодирование. Ключ в закодированном виде выглядит примерно так: sx--36l76l11l57l52l14l65l42l56l31l — вариация на тему кодов символов. Первые символы строки sx-- нужны для того, чтобы сохранить возможность передачи конструктору незакодированного секретного ключа — если строка начинается с sx--, значит, ключ закодирован, иначе ключ передан открытым текстом. Почему такое простое кодирование? Да потому что это защита от дурака, не более. От юного хацкера, только что прочитавшего очередную статью в известном журнале, этот метод поможет уберечься, а от квалифицированного разработчика, знающего основы JavaScript, никакое шифрование не спасет, потому что в конечном итоге, скрипту-то секрет нужен в открытом виде, а значит и функция для расшифровки должна быть в коде.

    Собственно, ничего необычного в библиотеке-то и нет. Но от рутинной работы при разработке приложений, библиотека спасет. Как и обещал, ниже ссылка на скачивание.

    Библиотека: vk_api-1.4.4.zip.
    Страница проекта на Google Code: http://vk-jsapi.googlecode.com/
    Новые версии и полное описание.

    Support the author
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 38

      –2
      А уже работающие приложения с использования Javascript API можно посмотреть?
      –4
      В чем проблема использовать API через php?
        –4
        Секаса осенна многа, насяльника.

        «Пользовательские» методы API используют куки для проверки авторизации. Передать эти куки на свой домен через iframe — отдельная проблема.
          –2
          Не труъ, аякс рулит. Сам сначала пытался через пхп сделать, потом заюзал скрипт автора и я понял, что на аяксе будет гараздо проще. А пхп там все равно пригодился, с методами secure.* не секьюрно через аякс работать.
            0
            А почему бы не обращатся к своему сценарию на PHP через AJAX? И сделать чтобы свой сценарий на PHP высчитывал «эти» подписи?
              +4
              Да, это повысит безопасность приложения — секретный ключ больше не будет фигурировать в клиентской части ни в каком виде. И если это критично, то можно так и делать.

              Однако, в этом нет никакой необходимости. Секретный ключ, на самом деле, никакой не секретный, а подпись ни от чего не защищает. Единственное, что можно сделать, украв чужой ключ — это притвориться другим приложением. Но что это даст потенциальному злоумышленнику?

              Все финансовые операции проводятся через механизм защищенного взаимодействия, в котором участвует совершенно другой ключ, не фигурирующий на стороне клиента. Кроме того, приложение выполняется в контексте определенного пользователя, так что навредить другому пользователю, злоумышленник не сможет — только себе. Приложению злоумышленник тоже никак не навредит, потому что от имени администратора он все равно не сможет никакие API-функции выполнить.

              В общем, красть секретный ключ и подделывать подпись совершенно бесполезно. И ничего страшного, даже если этот ключ будет в тексте в открытом виде. Вообще, из Flash-приложений этот ключ тоже извлекается, при желании.
          0
          Объясните плз как без JSONP получать ответы api-сервера вконтакте. Насколько я понимаю, json-ответ так и приходит в json-формате, без вызова какого-либо метода. То есть по событию загрузки динамически созданного тега script приложение выкинет JS ошибку. То есть это будет равноценно тому, что у нас на странице появится что-то типа:
          <script>
          {answer: [some_answer_in_vk_format]}
          </script>


          а не
          <script>
          hadle_answer({answer: [some_answer_in_vk_format]});
          </script>

            0
            Все, понял, скачал ваш код. Про параметр callback нигде в доках vkontakte Iframe API не написано. Откуда вы про него узнали? Вы — Дуров? :)
              0
              Про параметр callback нигде в доках vkontakte Iframe API не написано.

              О, да. По началу, это меня выбило из колеи. Обнаружил чисто случайно, когда экспериментировал с jQuery.
                0
                Параметр callback — стандартный в JSONP
                  0
                  Речь шла о get-параметре callback в API вконтакта, это во первых. Во вторых, в jsonp нет никаких стандартных параметров, callback там уже обрамляет json-ответ.
                    0
                    Я прекрасно понял о чем идет речь. Насчет стандартной я преувеличил, но например в JQuery — по умолчанию передается параметр с названием callback для того, чтобы реализовать кросс доменный JSONP запрос.
              –2
              А мне объясните — что толкает вас (разработчики) на переход с одного единого файла (swf) на сотню (html, css, js, jpg...)? — и на переход с того, что выполняется одинаково на любом браузере, на чехарду с парком браузеров пользователей(?) В чём есть огромнейшее преимущество перед SWF, что вы готовы на такой геморрой?
                +1
                А мне объясните — что толкает вас (разработчики) на переход с одного единого файла (swf) на сотню (html, css, js, jpg...)?

                Про всех говорить не стану, но лично мне не нравится то, что в линуксе еще с ранних версий Flash-плагина существует баг, не позволяющий вводить в текстовые поля русский текст. Вернее даже, не то, что мне это не нравится, а из-за этого я вообще не могу использовать ни одну флешку, в которой надо вводить русский текст.

                  +1
                  этот баг появляется только в случае если у флешки wmode=opaque
                    0
                    в beta-версии исправили этот баг, так что ждём
                      0
                      Хорошая новость. Ждем.
                    0
                    Кстати, библиотеку вполне можно портировать на Actionscript.
                    Я просто не интересовался, возможно, там уже что-то подобное есть. Все же, Flash-приложения доступны там уже давным давно.
                    0
                    А не подскажите, есть ли готовый пример вывода РСС с своего сайта, например?
                      0
                      А сильверлайт, интересно, поддерживается кроссдомейн.иксэмэлем?..
                        +1
                        Вообщем, под IE8 баг — правильная часть кода:
                        function decodeSecret(api_secret) {

                        // decode api_secret
                        if (api_secret.substring(0, 4) == 'sx--') {

                        var api_secret_decoded = '';
                        var char_code = '';
                        var code = '';
                        for ( var i = 4; i < api_secret.length; i++) {
                        // form char code
                        code = api_secret.substring(i,i+1);
                        if (code != 'l') {
                        char_code += code;
                        } else {
                        api_secret_decoded += String
                        .fromCharCode(parseInt(char_code) + 31);
                        char_code = '';
                        }
                        }
                        return api_secret_decoded;

                        // api_secret is not encoded, so just return it
                        } else {
                        return api_secret;
                        }
                        }
                          0
                          Спасибо за замечание.

                          Поначалу, честно говоря, был озадачен. Полез в доки, естественно — оказалось, точно:
                          Treating the string as an array is not part of ECMAscript; it's a Javascript feature (and not supported in all browsers)


                          Единственное, чем писать api_secret.substring(i,i+1), наверное, проще будет писать api_secret.charAt(i)
                            0
                            А это точно кроссбраузерно? Потому как сейчас я даже с айфона могу загружать приложение своё.
                              0
                              А это точно кроссбраузерно? Потому как сейчас я даже с айфона могу загружать приложение своё.

                              У меня нет IE, так что проверить мне сложнее. Но вообще, как бы, да. Квадратные скобки не являются частью стандарта ECMAscript, и не обязаны поддерживаться всеми браузерами (каюсь, не знал). А метод charAt() должен поддерживаться всем. Так или иначе, надо проверять.
                              0
                              А вообще, спасибо за проделанную работу! )
                                +1
                                кстати, протестировать на екмаскриптовость помогает jslint.com.
                                там ещё и ворнинги полезные есть.
                              0
                              Подскажите, тут такой вопрос. Есть скрипт:
                              var api = new vk_api('...','..');
                              api.params.viewer_id = <?php echo $_GET['viewer_id']; ?>;
                              api.params.api_id = 123;
                              api.call('isAppUser',function(data) {alert(123);});

                              Все вроде просто. При вызове call firebug говорит:
                              MD5 is not defined
                              [Break on this error] parameters.sig = MD5(sig);

                              Пытался разобраться с исходниками — не вышло. Как лечить?
                                0
                                Подсунул MD5 прямо в скрипт — заработало. Иначе — нет.
                                  0
                                  Вы неверно работаете со скриптом. Дело в том, что все манипуляции нужно проводить внутри callback-функции, выполняющейся при успешной инициализации. Т.е., в данном случае:

                                  var api = new vk_api(
                                      '...',
                                      function() {
                                          api.params.viewer_id = <?php echo $_GET['viewer_id']; ?>;
                                          api.params.api_id = 123;
                                          api.call('isAppUser', function(data) {alert(123);});
                                      }
                                  );
                                  


                                  Более того, устанавливать api.params.* через PHP — совершенно бессмысленно. Все эти параметры устанавливаются скриптом автоматически! Так что, если все далать еправильно, то Ваш скрипт должен быть таким:

                                  var api = new vk_api(
                                      '...',
                                      function() {
                                          api.call('isAppUser', function(data) {alert(123);});
                                      }
                                  );
                                  

                                  0
                                  Господин Колонист, спасибо вам за удобную библиотеку. Однако я вижу, что на странице xinit.ru/solutions/vkontakte-javascript-api-wrapper больше нет ничего, относящегося к vk_jsapi. Это смущает. :( Я уже взялся за проект, в котором vk_api вовсю используется, и хотелось бы быть уверенным (ишь, чего захотел!) что ваша библиотека не исчезнет из интернета и даже (еще более наглое пожелание) будет поддерживаться автором. :) В любом случае спасибо за удобную и полезную разработку, и надеюсь, что перемены на вашем сайте не означают конца vk-jsapi.
                                    0
                                    1. Из сети ничего не исчезнет, ибо Google Code: vk-jsapi.googlecode.com рулит. Свой сайт я тоже скоро приведу в порядок.

                                    2. Поддерживаться библиотека уже не будет, потому что Контакт сделал свое решение VK.api(). И я бы рекомендовал использовать именно решение самого Контакта. Тем более, что вызовы api.call() и VK.api() абсолютно идентичны.

                                    Тем не менее, библиотека работает. И если вы сделали приложение на ее основе, то оно будет прекрасно работать, пока вы не соберетесь его перестраивать.
                                      0
                                      Наткнулся на вашу библиотеку сегодня (разбирался с VK и искал, как там там добраться до params) и грешным делом решил, что это надстройка _над_ VK.api. А получается, что он ее полностью заменяет?
                                    0
                                    Да, полностью заменяет. Когда я ее делал, еще не было никакого VK.api, так что сейчас надо использовать контактовскую библиотеку.
                                      0
                                      Это был ответ на комментарий iamql

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