Сервис признаний в любви на Rails

в 6:36, , рубрики: oauth, ruby, ruby on rails, ruby on rails 3, vk.com, авторизация, День святого Валентина, социальные сети, метки: , , , , , ,

Мне очень нравятся сервисы вроде formsping.me, ask.fm тем, что у каждого пользователя есть своя маленькая страничка (например, ask.fm/popova) — и ты сразу знаешь к кому попал.
Мне и моему другу sborod пришла в голову идея сделать сервис для признаний в любви с похожей механикой:
1. Андрей заходит на сайт, логинясь через ВКонтакте, и вводит ссылку на профиль Марины, которая ему нравится.
Сервис признаний в любви на Rails
2. Ему генерируется собственная страничка howmuchiloveyou.ru/его короткое имя или id номер ВКонтакте, если короткое имя не указано.
3. Он копирует ссылку себе в статус ВКонтакте или на стену.
4. Всем перешедшим по ссылке, кроме Марины, говорят: «Я люблю не тебя». Если заходит Марина, то ей выводится: «Я тебя люблю! Андрей».

Вот такая простая на деле, но сложная для объяснения механика сервиса.
Дело было накануне Дня святого Валентина. У нас было два дня на всё: от проектирования и покупки домена до выкатывания и исправления мелких ошибок, поэтому были выбраны Рельсы.
В статье я вкратце расскажу про основные моменты создания приложения и некоторые особенности интеграции со ВКонтакте.

Поехали!

rails new howmuchiloveyou

Сначала мы прикинули схему путей, которая в итоге стала такой (после картинки — объяснение):
Сервис признаний в любви на Rails

У пользователя есть три варианта захода в приложение:
1. В "/welcome" из поисковых машин. Здесь будет отображаться общая информация о сервисе.
2. В "/show/:id" по чьей-то ссылке. Здесь будет отображаться любит ли тебя пользователь.
3. В "/choose_love", являющимся "/". Здесь залогиненный пользователь может выбрать свою любовь.

Серыми стрелками показаны автоматические редиректы, а зелеными — переходы по ссылкам.

Идем дальше. Создание модели:

rails generate model User vk_id:integer vk_screen_name:string love_id:integer

vk_screen_name — короткое имя ВКонтакте.
love_id — id, понравившегося человека.

Создаем приложение ВКонтакте:
Сервис признаний в любви на Rails

Создаем файл constants.rb в /config/initializers с константами приложения:

# ID приложения
APP_ID = 2800816

# Защищенный ключ
APP_SECRET = "top secret code"

Кладем в /lib библиотеку vk_oauth2.rb, позволяющую делать запросы к API ВКонтакте с помощью OAuth 2.0 (описание):

require 'net/http'

def vk_api_get(domain_name, params)
  http = Net::HTTP.new(domain_name, 443)
  http.use_ssl = true
  res = http.get(params)
  
  ActiveSupport::JSON.decode(res.body)
end

Все приготовления сделаны. Осталось рассказать о самом процессе авторизации через ВКонтакте. Чтобы поставить свою любовь, человеку предлагается залогиниться по ссылке:

<a href='http://oauth.vk.com/authorize?client_id=2800816&scope=friends&redirect_uri=http://howmuchiloveyou.ru/login&response_type=code'>Войти через ВКонтакте</a>

redirect_uri — это адрес, на который человек перенаправится после того, как даст разрешение использовать приложению личную информацию из ВКонтакте. В нашем случае — howmuchiloveyou.ru/login (которому передается параметр code, который используется для получения access_token, который будет использоваться для обращения к API).

Пишем в config/routes.rb:

match 'login' => 'users#login', :as => :login, :via => :get

Рассмотрим функцию login в контроллере users.rb:

def login

    # используем функцию из vk_oauth2.rb, чтобы распарсить, полученный JSON:
    login_parsed_json = vk_api_get(
      "oauth.vk.com",
      "/access_token?client_id=#{APP_ID}&" +
                    "client_secret=#{APP_SECRET}&" +
                    "code=#{params[:code]}"
    )
    
    # проверяем на ошибки:
    if login_parsed_json["error"].nil?
      @user = User.where(:vk_id  => login_parsed_json["user_id"]).first

      unless @user

        # создание нового юзера
        @user = User.new(:vk_id => login_parsed_json["user_id"])
        
        # запрос короткого имени. Если не указано пользователем, то ВКонтакте
        # пришлет screen_name в виде "id3455"
        @uservk_screen_name = vk_api_get(
          "api.vk.com", "/method/getProfiles?uid=#{login_parsed_json["user_id"]}&" +
                        "fields=screen_name&" +
                        "access_token=#{session[:access_token]}"
        )["response"][0]["screen_name"]
      end

      @user.save

      session[:user_id]          = @user.id
      session[:access_token]     = login_parsed_json["access_token"]

      redirect_to root_path
    else
      render :text => "Ошибка API vk.com: #{login_parsed_json['error']}." +
                      "Описание: #{login_parsed_json['error_description']}."
    end
  end

Имеем user_id и access_token в сессии, и теперь можно делать любые запросы к API ВКонтакте.

В итоге.
Как вы поняли, интегрировать ВКонтакте и Rail's проекты достаточно просто. Использовать одни и те же id и короткие имена на разных проектах очень приятно и вам, и пользователям.
Если кому-то интересно узнать подробности, то мы можем выложить исходники и/или расписать шаги более подробно в продолжении.

Спасибо за внимание!

Автор: ivanxo

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js