В начале этого года мне понадобилось работать с API ВКонтакте из rails-приложения. Увы, я не нашел сколько-нибудь устраивающего меня гема: где-то меня принуждали писать названия методов в camelCase (что в ruby-коде выглядит неестественно), где-то — обязательно проходить авторизацию через библиотеку (при том, что я использовал omniauth) и вообще везде для обращений к API использовался захардкоденный
Так появился на свет vkontakte_api. Рельсовый проект, послуживший поводом для написания данной библиотеки, уже успел почить — но гем живет и продолжает развиваться, в июле достигнув версии 1.0 (которая послужила поводом для значительных изменений). Используя
Посмотрим, как работать с API с помощью

В авторизации используются ID приложения и защищенный ключ, которые можно получить на странице редактирования приложения на ВКонтакте; а также redirect_uri, который будет описан далее. Эти параметры указываются в блоке
Настройки указываются следующим образом.
(на самом деле доступных настроек гораздо больше, но остальные тут не понадобятся)
Входить на сайт понадобится только через ВКонтакте, поэтому задействовать
Авторизация приложения на ВКонтакте использует протокол OAuth2. Это означает, что в результате авторизации будет получен токен доступа, который необходимо передавать при вызове методов API.
Схема его получения следующая: пользователь переходит по ссылке на страницу авторизации на ВКонтакте, соглашается дать приложению доступ к его (пользователя) данным, нажимая кнопку «Разрешить», и ВКонтакте редиректит его обратно в приложение, передавая в URL-е параметр
Для защиты от CSRF-атак протокол OAuth2 рекомендует передавать параметр
Итак, на странице входа нужно отобразить ссылку, ведущую на страницу авторизации приложения на ВКонтакте.
Тут нужно заметить, что по непонятным причинам ВКонтакте игнорирует
Когда пользователь подтвердит права приложения, он будет перенаправлен на указанный ранее в настройках
С помощью кода можно получить токен доступа, для этого нужно выполнить запрос к ВКонтакте. Пользователь в этом запросе никак не участвует — запрос идет прямо от нашего сервера к vk.com. Для этого
При выходе пользователя из нашего приложения просто почистим сессию:
Токен получен, можно работать с самим API.
Чтобы вызывать методы API, нужен объект
Далее можно вызывать методы на самом клиенте. Методы с составными именами вызываются по цепочке:
Все параметры API являются именованными и передаются в виде хэша, проиндексированного названиями параметров, например
Итак, нам нужна лента новостей, друзья и группы текущего пользователя. Также выведем имя и аватар пользователя в навигации. Для получения этих данных есть методы
Результаты методов возвращаются в виде
В навигации нужно показать аватар и имя текущего пользователя, полученные с ВКонтакте.
Здесь и далее используется ряд несложных хелперов (
Теперь выведем на страницу ленту новостей.
И, наконец, отобразим в сайд-баре друзей и группы пользователя.
Живое демо можно посмотреть здесь (осторожно, бесплатный heroku). Оно ничего не пишет на ВКонтакте — все методы API используются только для чтения данных и вывода их на страницу. Весь код лежит на Github.
Еще немного материалов по
Net::HTTP
, блокирующий реактор эвентмашины, на которую я тогда прицеливался. Также в плане документации почему-то все было очень грустно, и приходилось постоянно читать исходники.Так появился на свет vkontakte_api. Рельсовый проект, послуживший поводом для написания данной библиотеки, уже успел почить — но гем живет и продолжает развиваться, в июле достигнув версии 1.0 (которая послужила поводом для значительных изменений). Используя
faraday
, библиотека поддерживает вызов любых методов API, загрузку файлов на сервера ВКонтакте и опциональную авторизацию, не принимая за программиста решения, упомянутые в предыдущем абзаце.Посмотрим, как работать с API с помощью
vkontakte_api
. В качестве примера сгодится несложное веб-приложение, отображающее на странице ленту новостей (API-метод newsfeed.get), список друзей (friends.get) и групп (groups.get) пользователя, прошедшего OAuth2-авторизацию. А выглядеть это будет примерно так:
Настройка
В авторизации используются ID приложения и защищенный ключ, которые можно получить на странице редактирования приложения на ВКонтакте; а также redirect_uri, который будет описан далее. Эти параметры указываются в блоке
VkontakteApi.configure
, который удобно разместить в config/initializers/vkontakte_api.rb
; в rails-приложении можно сгенерировать этот файл с настройками по умолчанию с помощью встроенного генератора.$ rails generate vkontakte_api:install
Настройки указываются следующим образом.
# config/initializers/vkontakte_api.rb
VkontakteApi.configure do |config|
config.app_id = '123' # ID приложения
config.app_secret = 'AbCdE654' # защищенный ключ
config.redirect_uri = 'http://vkontakte-on-rails.herokuapp.com/callback'
end
(на самом деле доступных настроек гораздо больше, но остальные тут не понадобятся)
Авторизация
Входить на сайт понадобится только через ВКонтакте, поэтому задействовать
omniauth
будет нецелесообразно — используем возможности vkontakte_api
.Авторизация приложения на ВКонтакте использует протокол OAuth2. Это означает, что в результате авторизации будет получен токен доступа, который необходимо передавать при вызове методов API.
Схема его получения следующая: пользователь переходит по ссылке на страницу авторизации на ВКонтакте, соглашается дать приложению доступ к его (пользователя) данным, нажимая кнопку «Разрешить», и ВКонтакте редиректит его обратно в приложение, передавая в URL-е параметр
code
. Далее приложение, используя этот код, получает токен и user_id
пользователя отдельным запросом и сохраняет их в сессии.Для защиты от CSRF-атак протокол OAuth2 рекомендует передавать параметр
state
с неугадываемым значением при отправке пользователя на авторизацию, предварительно сохранив его в защищенном месте; а при возвращении пользователя сверять полученный в параметрах state
с сохраненным значением.Итак, на странице входа нужно отобразить ссылку, ведущую на страницу авторизации приложения на ВКонтакте.
vkontakte_api
предоставляет хелпер VkontakteApi.authorization_url
для генерации URL этой страницы; в параметрах нужно передать scope
— это права, которые получит приложение, в виде массива символов (или же строки с названиями, разделенными запятыми) — и описанный выше state
.# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
# генерируем случайный state
srand
session[:state] ||= Digest::MD5.hexdigest(rand.to_s)
# и URL страницы авторизации
@vk_url = VkontakteApi.authorization_url(scope: [:friends, :groups, :offline, :notify], state: session[:state])
end
end
<!-- app/views/sessions/new.html.erb -->
<%= link_to @vk_url, class: 'btn btn-primary' do %>
<i class="icon-home icon-white"></i>
Войти через ВКонтакте
<% end %>
Тут нужно заметить, что по непонятным причинам ВКонтакте игнорирует
state
, если в scope
не указан notify
.Когда пользователь подтвердит права приложения, он будет перенаправлен на указанный ранее в настройках
redirect_uri
(содержащий путь к SessionsController#callback
), при этом в URL будут переданы параметры state
и code
. Как говорилось чуть выше, state
нужно сверить с уже сохраненным; а на code
остановимся поподробнее.С помощью кода можно получить токен доступа, для этого нужно выполнить запрос к ВКонтакте. Пользователь в этом запросе никак не участвует — запрос идет прямо от нашего сервера к vk.com. Для этого
vkontakte_api
также предоставляет хелпер — VkontakteApi.authorize
, единственный параметр — пресловутый code
.# encoding: utf-8
class SessionsController < ApplicationController
def callback
# проверка state
if session[:state].present? && session[:state] != params[:state]
redirect_to root_url, alert: 'Ошибка авторизации, попробуйте войти еще раз.' and return
end
# получение токена
@vk = VkontakteApi.authorize(code: params[:code])
# и сохранение его в сессии
session[:token] = @vk.token
# также сохраним id пользователя на ВКонтакте - он тоже пригодится
session[:vk_id] = @vk.user_id
redirect_to root_url
end
end
При выходе пользователя из нашего приложения просто почистим сессию:
class SessionsController < ApplicationController
def destroy
session[:token] = nil
session[:vk_id] = nil
redirect_to root_url
end
end
Токен получен, можно работать с самим API.
Вызов методов API
Чтобы вызывать методы API, нужен объект
VkontakteApi::Client
. В конструктор нужно просто передать токен.Далее можно вызывать методы на самом клиенте. Методы с составными именами вызываются по цепочке:
vk.users.get(params)
. В соответствии с принятыми в ruby-сообществе соглашениями названия методов пишутся в snake_case
: метод API likes.getList
можно вызвать как vk.likes.get_list
.Все параметры API являются именованными и передаются в виде хэша, проиндексированного названиями параметров, например
vk.users.get(uid: 1)
. Если API ожидает получить в параметре коллекцию объектов, перечисленных через запятую, то их можно передать в виде массива — vkontakte_api
склеит его автоматически (аналогично обрабатывается параметр scope
в авторизации). При этом вместо строк можно использовать символы.Итак, нам нужна лента новостей, друзья и группы текущего пользователя. Также выведем имя и аватар пользователя в навигации. Для получения этих данных есть методы
newsfeed.get
, friends.get
, groups.get
и users.get
соответственно (последний будем вызывать, передавая параметром id нашего пользователя). Результат newsfeed.get
содержит отдельно сами новости, содержащие id пользователей и групп, и отдельно массивы с упомянутыми пользователями и группами; не показанный здесь метод MainController#process_feed
добавляет к каждой новости ее источник (пользователь или группа, написавшая пост) под ключом source
.class MainController < ApplicationController
def index
# сначала создадим клиент API
vk = VkontakteApi::Client.new(session[:token])
# теперь получим текущего юзера
@user = vk.users.get(uid: session[:vk_id], fields: [:screen_name, :photo]).first
# его друзей
@friends = vk.friends.get(fields: [:screen_name, :sex, :photo, :last_seen])
# отдельно выберем тех, кто в данный момент онлайн
@friends_online = @friends.select { |friend| friend.online == 1 }
# группы
@groups = vk.groups.get(extended: 1)
# первый элемент массива - кол-во групп; его нужно выкинуть
@groups.shift
# и ленту новостей
raw_feed = vk.newsfeed.get(filters: 'post')
# обработанную в отдельном методе
@newsfeed = process_feed(raw_feed)
end
end
Результаты методов возвращаются в виде
Hashie::Mash
— это расширение стандартного Hash
из гема hashie, позволяющее обращаться к элементу через метод, название которого соответствует ключу этого элемента в хэше (user.name == user[:name]
).В навигации нужно показать аватар и имя текущего пользователя, полученные с ВКонтакте.
<%= link_to vk_url(@user), target: '_blank' do %>
<%= image_tag(@user.photo, width: 20) %>
<%= "#{@user.first_name} #{@user.last_name}" %>
<% end %>
Здесь и далее используется ряд несложных хелперов (
vk_url
, name_for
, avatar_for
итд), определенных в приложении — все они достаточно тривиальны, при желании можно почитать код здесь.Теперь выведем на страницу ленту новостей.
<!-- app/views/main/index.html.erb -->
<% @newsfeed.each do |item| %>
<tr>
<td>
<%= link_to vk_url(item.source), target: '_blank' do %>
<%= image_tag avatar_for(item.source) %>
<% end %>
</td>
<td class="wide">
<div class="pull-right"><%= formatted_time_for(item.date) %></div>
<%= link_to name_for(item.source), vk_url(item.source), target: '_blank' %>
<p><%=raw render_links(item.text) %></p>
<% item.attachments.each do |attachment| %>
<%= render 'attachment', attachment: attachment %>
<% end if item.attachments? %>
</td>
</tr>
<% end %>
<!-- app/views/main/_attachment.html.erb -->
<p>
<% case attachment.type %>
<% when 'link' %>
<%= link_to attachment.link.title, attachment.link.url, target: '_blank' %>
<% when 'photo' %>
<%= image_tag attachment.photo.src_big %>
<% when 'video' %>
<%= image_tag attachment.video.image_big %>
<% end %>
</p>
<div class="clearfix"></div>
И, наконец, отобразим в сайд-баре друзей и группы пользователя.
<!-- app/views/main/_sidebar.html.erb -->
<div class="tab-pane active" id="friends_online">
<h6>Друзья онлайн</h6>
<%= render 'friends', friends: @friends_online %>
</div>
<div class="tab-pane" id="friends">
<h6>Все друзья</h6>
<%= render 'friends', friends: @friends %>
</div>
<div class="tab-pane" id="groups">
<h6>Группы</h6>
<%= render 'groups' %>
</div>
<!-- app/views/main/_friends.html.erb -->
<table class="table">
<% if friends.empty? %>
<tr>
<td>Никого не найдено</td>
</tr>
<% else %>
<% friends.each do |friend| %>
<tr>
<td>
<%= link_to image_tag(friend.photo), vk_url(friend), target: '_blank' %>
</td>
<td class="wide">
<i class="icon-user"></i>
<%= link_to "#{friend.first_name} #{friend.last_name}", vk_url(friend), target: '_blank' %>
<br />
<%= online_status(friend) %>
</td>
</tr>
<% end %>
<% end %>
</table>
<!-- app/views/main/_groups.html.erb -->
<table class="table">
<% if @groups.empty? %>
<tr>
<td>Вы не состоите в группах</td>
</tr>
<% else %>
<% @groups.each do |group| %>
<tr>
<td>
<%= link_to image_tag(group.photo), vk_url(group), target: '_blank' %>
</td>
<td class="wide">
<i class="icon-comment"></i>
<%= link_to group.name, vk_url(group), target: '_blank' %>
</td>
</tr>
<% end %>
<% end %>
</table>
Живое демо можно посмотреть здесь (осторожно, бесплатный heroku). Оно ничего не пишет на ВКонтакте — все методы API используются только для чтения данных и вывода их на страницу. Весь код лежит на Github.
Еще немного материалов по vkontakte_api
- домашняя страница проекта
- README.md
- RDoc-документация со 100% покрытием
- более продвинутый пример использования vkontakte_api (асинхронное приложение-мессенджер на вебсокетах)