#概要
見習いエンジニアのナツキです。RailsのAPIモードを使ってVue.jsのシンプルなSPAを作ります。チュートリアルというよりは、開発ログです。この章では、RAILS部分がメインです。まだ未完成なので随時更新していきます。
#環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.1
BuildVersion: 17B1003
Rails 5.2.0
ruby 2.3.1
※Qiita一件目の記事です!よろしくお願いします。
目標物
名前:hogekai
概要:Twitter上で気軽に〇〇会の開催を共有・募集することができるウェブサービス。
フレームワーク・ライブラリ・Gem
Rails 5 API
devise_token_auth/omniauth/omniauth-twitter
Vue/Vuex/Vue-router/Axios
Foreman/Webpacker
Element
対象者
・RailsとVue.jsの基本を理解しているが、APIやSPAの開発にまだ慣れていない人。
・トークン認証について理解を深めたい人。
・Elementを使ってスピード感のある開発をしたい人
・hogekaiの裏側が気になる人。
目次
①Rails編
- Vue.js用のRails API アプリを作成する(forman + webpacker)
- Rails APIにトークン認証を実装する(devise_token_auth)
- Rails APIにTwitter認証を実装する(device_token_auth + omniauth)
- 本番環境にデプロイ(Heroku)
②Vue編
- SPAの動線をつくる(Vue-router)
- Vue.js側のトークン認証を実装する(Axios + Vuex)
- 本番環境にデプロイ(Heroku)
③その他(Element-UI)
- landing pageを作る
- Event投稿機能の作成(CRUD機能/form/modal)
- Twitterイベント共有
- Twitterメッセージ機能の作成
- 本番環境にデプロイ(Heroku)
[未定]RailsでTwitter共有用の画像を動的に作成する(carrierwave)
- 画像をAWS S3に保存する。
- 画像を動的に変更して保存する。
- 本番環境にデプロイ(Heroku)
注意
このnoteはチュートリアルというよりは、開発ログに近いものです。僕が参考にした記事をいくつか貼っていくので、わからなくなったらそちらにアクセスしてください。※英語の記事もガンガン紹介していきます!
1. Vue.js用のRails API アプリを作成する
foremanとwebpackerを使ってAPIを実装していきます。
ボイラープレート(任意)
すでにベーシックな初期設定が済んでいるプロジェクトを見つけました。教科書通りベーシックな部分が網羅されています。テンポよく開発したい方はこれをつかうのもありです。(今回は学習もかねて一から作ります)
参考記事:Rails 5.1 + Vue.js + Vuex + vue-routerの初期設定
データベースの準備
herokuへのデプロイをスムーズに行う為に最初からpostgreqlで実装していきます。
参考記事:MacのRailsアプリでPostgreSQLを使う方法
DB設定の流れ
# PostgreSQLのインストール(MAC版)
$brew install postgresql
# バージョンの確認・インストールされたかの確認
$ postgres --version
postgres (PostgreSQL) 10.4
# データベースクラスタの作成
$initdb /usr/local/var/postgres -E utf8
# PostgreSQLの起動
# ctr + cで終了
$postgres -D /usr/local/var/postgres
# データベースの確認
$psql -l
# PostgreSQLがデフォルトで参照するデータクラスを設定
$export PGDATA=/usr/local/var/postgres
# ユーザーの作成
$createuser hogekai_admin
# ユーザーの確認
$psql -q -c'select * from pg_user' postgres
# データベースの作成
$createdb hogekai_dev -O hogekai_admin
# 新しく作ったデータベースの確認
$ psql -q -c'select * from pg_user' hogekai_dev
# ユーザー権限の確認
# ここで \du と打つとユーザーの権限を確認。
# ここで \q と打つと通常の画面に戻る。
$psql -U hogekai_admin hogekai_dev
# ユーザー権限の変更(natsukingは自分のroot_userの名前)
# ここで ALTER ROLE hogekai_admin WITH Superuserと打つとhogekai_adminにsuperuserの権限を与える。
# ALTER USER hogekai_admin WITH PASSWORD 'hogekai_password' と打つとパスワードの設定・変更。
# \du と打つとユーザーの権限を確認。
# \q と打つと通常の画面に戻る。
$psql -U natsuking hogekai_dev
ここで設定した情報はあとでRails側で使います。
Rails プロジェクトの作成
参考記事: Vue.jsとRailsでTODOアプリのチュートリアルみたいなものを作ってみた
記事の通りに進まない部分も多いです。
$rails new hogekai --webpack=vue --database=postgresql
database.ymlにデータベースの情報を書き足す
...
development:
<<: *default
database: hogekai_dev
username: hogekai_admin
password: hogekai_password
PostgreSQLを起動(もし既に動いていない場合)
$postgres -D /usr/local/var/postgres
foremanを追加(RailsとVue両方の開発サーバーを管理するgem)
// Gemfile
group :development do
...
gem 'foreman'
end
$bundle install
Procfileの作成(foremanの設定書のようなもの。root directory以下に作る)
// Procfile
rails: bundle exec rails server
webpack: ./bin/webpack-dev-server
foremanを起動するコマンド
$ foreman start
この状態で**http://localhost:5000/**にアクセスすると、RailsのHello World画面が表示されるはずです。
次に、ルートパスで表示されるViewとControllerを作成します。
$rails g controller home about
Rails.application.routes.draw do
root 'home#about'
end
これでもう一度localhost:5000にアクセスすると下の様な画面が表示されるはずです。
今度はデフォルトで生成されるapp.vueファイルを表示させて見ましょう。
<!DOCTYPE html>
<html>
<head>
<title>Hogekai</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'hello_vue' %>
</head>
<body>
<div id='hello'>
{{message}}
<app></app>
</div>
</body>
</html>
hello_vue.jsには、いろいろとコードが書かれていますが、必要なコードだけコメントインして、あとは削除。(Turbolinkを使いたい人は、一番下のコードを代わりにコメントイン。)
import Vue from 'vue/dist/vue.esm'
import App from '../app.vue'
document.addEventListener('DOMContentLoaded', () => {
const app = new Vue({
el: '#hello',
data: {
message: "Can you say hello?"
},
components: { App }
})
})
この時点で、下の画面が表示されたら成功です。
2. Rails APIにトークン認証を実装する
hogekaiでは、twitter認証しか使いませんが、普通のパスワード認証の実装も説明していきます。
参考記事: devise token auth を使って簡単に早くAPIを作る 1
必要な部分だけ参照。
Gemのインストール
// Gemfile
...
gem 'omniauth'
gem 'devise'
gem 'devise_token_auth'
gem 'rack-cors'
$bundle install
devise_token_authをインストール&User modelの作成
$rails g devise_token_auth:install User auth
migrationファイルの編集
confirmableの部分を今回はコメントアウトします。
## db/migrate/生成されたファイルの名前
class DeviseTokenAuthCreateUsers < ActiveRecord::Migration[5.2]
def change
create_table(:users) do |t|
## Required
t.string :provider, :null => false, :default => "email"
t.string :uid, :null => false, :default => ""
## Database authenticatable
t.string :encrypted_password, :null => false, :default => ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.boolean :allow_password_change, :default => false
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0, :null => false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
## User Info
t.string :name
t.string :nickname
t.string :image
t.string :email
## Tokens
t.json :tokens
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, [:uid, :provider], unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
$rails db:migrate
認証用のAPI controllerを作成
$ rails g controller api/v1/auth/registrations
// controllers/api/v1/auth/registrations_controller.rb
module Api
module V1
module Auth
class RegistrationsController < DeviseTokenAuth::RegistrationsController
private
def sign_up_params
params.permit(:name, :email, :password, :password_confirmation)
end
def account_update_params
params.permit(:name, :email)
end
end
end
end
end
パスを調整
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
scope :v1 do
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
registrations: 'api/v1/auth/registrations'
}
end
end
root 'home#about'
end
パスを確認 api/v1/auth/registrations#newとかが表示されれば成功
$rails routes
initializerの設定
## config/initializers/devise_token_auth.rb
## falseに設定
config.change_headers_on_each_request = false
## コメントイン 1.monthでもよい
config.token_lifespan = 2.weeks
## コメントイン
config.headers_names = {:'access-token' => 'access-token',
:'client' => 'client',
:'expiry' => 'expiry',
:'uid' => 'uid',
:'token-type' => 'token-type' }
CSRF対策(これがないとAPIリクエストでエラーがでる)
class ApplicationController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
protect_from_forgery
end
Postman
実際にAPIのリクエストを出して、jsonが返って来ることを確認していきます。今回はpostmanを使います。
参考記事: APIの開発がむちゃくちゃ捗る「Postman」の使い方
今回テストするJSONデータ
{
"name": "山口なつき",
"email": "hogekai@example.com",
"password": "123456789"
}
上記のデータを下のスクショの様にセットしてSendをクリック。
気をつける点:
1 POSTに設定してある?
2 http://localhost:5000/api/v1/auth
3 JSON(application/json)を選択してる?
次のように結果が返ってくればOK
ログイン
これでUserが登録されました。次はログインです。
{
"email": "hogekai@example.com",
"password": "123456789",
"password_confirmation": "123456789"
}
この内容を先ほどと同じ様にPOSTします。ただし今回はlocalhost:5000/api/v1/auth/sign_inにリクエストを送ります。
こんな感じのものが返ってきたら成功です。ここで重要なのが、返ってきたHeadersの情報です。クリックした中身を確認します。
- access-token
- client
- uid
の三つの値をメモります。これこそが、ユーザーのログイン状態を保つための重要な情報です。
パスワードの変更
パスワードの変更に挑戦します。先ほどログインして返ってきたヘッダーの情報を使います。
access-token: C3e3Ok6aJzSE5TFG0N3-bg
client: cNOPygTl4G9Px0-uFoz6Zw
uid: hogekai@example.com
今度は, localhost:5000/api/v1/auth/passwordにPUTします。
今回はPOSTではなくPUTです。
{
"password": "987654321",
"password_confirmation": "987654321"
}
これでうまくいくと、下の様なJSONが返ってきます。
ユーザー情報の変更
今度はユーザー情報を変更してみます。localhost:5000/api/v1/auth に PUT。headerには、前回と同じuid, client, access-tokenを入れています。名前をひらがなから漢字に変えます。
{
"name": "山口 夏生"
}
これで下のJSONが返ってきたら成功。
パスワードを忘れた際の変更処理は、割愛します。参考記事を読んでください。
3. Rails APIにTwitter認証を実装する
パスワード認証ができたので、twitter認証にうつります。device_token_auth + omniauthで実装していきます。twitterのトークン認証の記事は地味に少なくて、苦労しました。
omniauth omniauth_callbacks_controllerソースコード
omniauth-twitter 公式ドキュメント
参考記事: Rails5のAPIモードでdevise_token_authとomniauthを使ったログインを試す
Gemのインストール
dotenvは、APIのSECRET等の情報を安全に管理するために使います。好みです。
// Gemfile
gem 'omniauth-twitter'
gem 'dotenv-rails', '~> 2.1', '>= 2.1.1'
$bundle install
// 古いバージョンのdotenvがインストールされる可能性があるので
$bundle update
Twitterアプリとdotenv
参考記事:【Rails】『dotenv』で環境変数を管理する方法
Twitter アプリの登録+そこで入手するAPP KEYとAPP SECRETが必要になるので、記事を参考に登録を進めてください。
参考記事:リニューアル後のTwitterアプリの作成、登録法 APIキーの取得法
僕のはこんな感じ。localhostでは通りませんでした。
本番環境用のアプリもあとで登録するので、hogekai_devに変えました。
そこで入手した情報を.envファイルの中に書き込みます。
// .env
TWITTER_KEY = "自分のConsumer Key"
TWITTER_SECRET = "自分のConsumer Secret"
initializerの設定
initializer以下にomniauth.rbというファイルを作成して初期設定を書き込みます。
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, ENV.fetch("TWITTER_KEY"), ENV.fetch("TWITTER_SECRET"), callback_url: "http://127.0.0.1/api/v1/auth/twitter/callback"
end
モデルの編集
モデルにも設定を付け加えます。
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers: [:twitter]
twitter認証用のルートを追加します。
パスの調整
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
scope :v1 do
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
registrations: 'api/v1/auth/registrations',
omniauth_callbacks: 'api/v1/auth/omniauth_callbacks'
}
end
end
root 'home#about'
end
omniauth_callbacks_controllerの上書き
callback用のcontrollerをcontrollers/api/v1/auth/以下に作成します。もともとあるコードを上書きしています。このコードが最善のコードなのかはかなり怪しいですが、色々と試行錯誤しているうちにこれにたどり着きました。あとで説明するコードもあるのでコメントを参考にしてください。
module Api
module V1
module Auth
class OmniauthCallbacksController < DeviseTokenAuth::OmniauthCallbacksController
// ①セッションを有効化
skip_before_action :skip_session
def redirect_callbacks
super
end
def omniauth_success
super
update_auth_header
end
def omniauth_failure
super
end
protected
// ②credentialを保存
def get_resource_from_auth_hash
super
@resource.credentials = auth_hash["credentials"]
clean_resource
end
def render_data_or_redirect(message, data, user_data = {})
if Rails.env.production?
if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
render_data(message, user_data.merge(data))
elsif auth_origin_url
redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true))
else
fallback_render data[:error] || 'An error occurred'
end
else
// わかりやすい様に開発時はjsonとして結果を返す
render json: @resource, status: :ok
end
end
// twitterから取得する絵文字を取り払うメソッドたち DBエラーが起きるときにコメントイン
// mysqlだと起きやすい
def clean_resource
@resource.name = strip_emoji(@resource.name)
@resource.nickname = strip_emoji(@resource.nickname)
end
def strip_emoji(str)
str.encode('SJIS', 'UTF-8', invalid: :replace, undef: :replace, replace: '').encode('UTF-8')
end
end
end
end
end
credentialsコラムの追加
Twitter APIを経由してタイムラインとかに投稿したいことを想定して、twitter認証で返って来るアクセストークンをcredentialsというコラムに保存します。
credentialsコラムを作ります。
$rails g migration addColumnToUsers credentials:text
$rails db:migrate
application.rbを編集。
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Hogekai
class Application < Rails::Application
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore
config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*',
:headers => :any,
:expose => ['access-token', 'expiry', 'token-type', 'uid', 'client'],
:methods => [:get, :post, :options, :delete, :put]
end
end
end
end
application_controller.rbを編集
class ApplicationController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
protect_from_forgery
before_action :skip_session
protected
def skip_session
request.session_options[:skip] = true
end
end
これで準備は整ったはずです。謎なコードも多いかと思いますが、公式ドキュメントやグーグルの記事を読んで理解してください。
ブラウザーで確認
ではブラウザーから確認して見ましょう。http://127.0.0.1:5000/api/v1/auth/twitter でアクセスします。
こんな感じの画面がでるはずです。
これでauthorize appをクリックして下の様なJSONが返ってくれば成功です。
ヘッダーの確認
パスワード認証の時と同じでヘッダーにはログイン状態をキープするためのトークン情報(uid, client, access-token)が入ってます。Google Chromeの開発ツールを使うと確認できます。
参考記事:Chromeデベロッパー・ツールを使ってヘッダー情報を確認する
これでTwitter認証もできました。今回作ったAPIをherokuでデプロイして見ましょう。
4. 本番環境にデプロイ
API側での認証機能は実装できたので、一度herokuにデプロイして本番環境で稼働してみます。
herokuのアカウントを持っていることを前提で進めます。
参考記事:挫折した人必見!HerokuでRailsアプリを公開する方法
heroku toolbaltのインストール
heroku toobaltが最新版であることを確認してください。僕の場合バージョンが古かったのでアップデートしました(Macを使ってます)。
$brew install heroku/brew/heroku
heroku サーバーの作成
$heroku create hogekai
デプロイ
まず自分のgitリポジトリーを最新の状態にアップデートします。
$git add .
$git commit -m 'add token authentication features to Rails API'
$git push origin master
環境変数を設定
これを行わないとデプロイするときにエラーがでます。
環境変数関連のコマンドはこちら [heroku] 環境変数の操作 から拝借しました。
# 環境変数一覧
$ heroku config
# 環境変数名を指定して参照
$ heroku config:get ENV_VAR_NAME
# 追加 (一応addも使えます)
$ heroku config:set ENV_VAR_NAME="value"
# 削除 (一応removeも使えます)
$ heroku config:unset ENV_VAR_NAME
では実際に環境変数を設定します。
$heroku config:set TWITTER_KEY="自分のTWITTER_KEY"
$heroku config:set TWITTER_SECRET="自分のTWITTER_SECRET"
デプロイ
$git push heroku master
トラブルシューティング
remote: Devise.secret_key was not set. Please add the following to your Devise initializer:
remote:
remote: config.secret_key = 'シークレットキー'
初期設定ファイルを編集します。
secret_keyは、devise_token_authではなく、devise側で処理するものなので、新しくDevise.setupを付け足しました。別のファイルを作ってもいいです。
DeviseTokenAuth.setup do |config|
...
end
# ここから下をつけたす
Devise.setup do |config|
config.secret_key = ENV.fetch('DEVISE_SECRET')
end
dotenvで管理します。
// .env
DEVISE_SECRET = 'シークレット'
herokuに環境変数を渡します。
$heroku config:set DEVISE_SECRET="シークレット"
リポジトリを更新します。
$git add .
$git commit -m 'add devise secret'
$git push origin master
もう一度デプロイしてみます。
$git push heroku master
いくつかwarningがでましたがうまく行きました。
DBのマイグレーション
$heroku run rake db:migrate
開く
$heroku open
うまくトップ画面が表示されました!
これでAPIも立ち上がっています。
パスワート認証のテスト
試しにポストマンでパスワード認証を行います。
新しいパスを使いましょう。
成功です!
ついでにパスワードの変更も試します。
Twitter認証のテスト
Twitterの認証は、ちょっと面倒です。callback urlが違うので開発用のものと本番環境用のアプリを別で登録します。今回は省略しますが、是非自身で挑戦してください。
本番用のKEYとSECRETを取得できたら、heroku側の環境変数を更新して完了です。
ローカルのconfigファイルをいじる方法もあるので、暇な人は調べてください。
まとめ
今回は認証機能をRails APIに実装しました。次回は、Vue.jsの方をがっつりいじって行きます。