Rails APIでUser周りの実装をしていきます。User周りの実装は基本的なアプリでは必須になってくると思うのでぜひ参考にしてください。
Docker環境上で開発しています。
のちに同じプロジェクト内で管理画面を開発することを考えたため、Rails APIでは開発していません。
駆け出しエンジニアですので間違っているところはコメントください。
Userテーブル
Userテーブルは以下の設計にしました
カラム名 | データ型 | 制約 | 説明 |
---|---|---|---|
id | integer | 主キー、オートインクリメント | ユーザーの一意な識別子 |
name | string | null: false | ユーザーの名前 |
string | null: false, 一意制約 | ユーザーのメールアドレス | |
password_digest | string | null: false | ハッシュ化されたパスワード |
created_at | datetime | null: false | レコード作成日時 |
updated_at | datetime | null: false | レコード更新日時 |
ルーティング
今回の実装はユーザーの新規登録、ログイン、退会、ログアウトを実装していきます。
HTTPメソッド | パス | コントローラ#アクション | 説明 |
---|---|---|---|
POST |
/v1/signup |
users#create |
ユーザーの新規登録 |
POST |
/v1/login |
sessions#create |
ユーザーのログイン(JWT発行) |
DELETE |
/v1/logout |
sessions#destroy |
ユーザーのログアウト |
DELETE |
/v1/profile |
users#destroy |
ユーザーの退会 |
- ユーザーの新規登録では、リクエストボディにname、email、passwordを含め、成功すると、登録されたユーザーの情報が返されます
- ユーザーのログインでは、リクエストボディにemailとpasswordを含め、認証に成功すると、JWTトークンが発行され、今後のリクエストでの認証に使用されます
- ユーザーのロウアウトでは、現在のセッションを終了し、ユーザーをログアウトさせます。このエンドポイントにアクセスすることで、JWTトークンを無効化し、以後のリクエストに対して認証を行えなくします
- ユーザーの退会では、ユーザーアカウントを削除します。このエンドポイントを利用することで、ユーザーは自分のアカウントを退会することができます。成功すると、関連するユーザーデータも削除されます
以下がroutsファイルです
Rails.application.routes.draw do
namespace :v1 do
post '/signup', to: 'users#create'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
delete '/profile', to: 'users#destroy'
end
end
期待するレスポンス
- POST /v1/signup
{
"message": "ユーザー登録が完了しました",
"user": {
"id": 1,
"name": "bar",
"email": "bar@example.com"
}
}
- POST /v1/login
{
"message": "ログインに成功しました。",
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0fQ.KzYrVPsdU0i8n1g5qoQRtadIj8CCYTJSbgof5nf-5HM"
}
- DELETE /v1/logout
{
"message": "ログアウトしました。。"
}
- DELETE /v1/profile
{
"message": "アカウントが削除されました。"
}
Userテーブルの作成
マイグレーションの作成
docker-compose run web rails generate model User name:string email:string:uniq password_digest:string
このコマンドにより、Userテーブルのマイグレーションファイルが生成されます。name、email、password_digestのカラムが追加されます。
マイグレーションファイルの修正
生成されたマイグレーションファイルを開き、emailカラムにユニーク制約を明示的に追加して、以下のように修正します。
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false, unique: true
t.string :password_digest, null: false
t.timestamps
end
add_index :users, :email, unique: true
end
end
マイグレーションの実行
docker-compose run web rails db:migrate
これでUserテーブルがデータベースに作成されます。
また、userモデルも作成されます
bcryptの有効化
- Gemfileにbcryptを追加
まず、プロジェクトのGemfileにbcryptを追加します。bcryptはパスワードのハッシュ化に必要です。
gem 'bcrypt', '~> 3.1.7'
- bundle installを実行
docker-compose run web bundle install
- build
docker-compose build
- Userモデルにhas_secure_passwordを追加
class User < ApplicationRecord
has_secure_password
validates :name, presence: true
validates :email, presence: true, uniqueness: true
end
コントローラの作成
Apiコントローラ
APIモードでないRailsアプリケーションで、JSONリクエストを扱うためにapiコントローラでCSRFを防止します。apiで扱われるコントローラはapiコントローラを継承します。
また、ログイン中のユーザーをリクエストヘッダーから取得するためのcurrent_userメソッドを作成します。
docker-compose exec web rails generate controller api/v1/Api
module Api
module V1
class ApiController < ApplicationController
protect_from_forgery with: :null_session
def current_user
token = request.headers['Authorization']&.split(' ')&.last
return nil unless token
begin
decoded_token = JWT.decode(token, Rails.application.secrets.secret_key_base)[0]
user_id = decoded_token['user_id']
@current_user ||= User.find(user_id)
rescue JWT::DecodeError
nil
end
end
end
end
end
Usersコントローラー
ユーザーの新規登録、退会を管理するために、UsersControllerを作成します
docker-compose exec web rails generate controller api/v1/Users
コントローラーを編集します
module Api
module V1
class UsersController < ApiController
# ユーザー登録
def create
user = User.new(user_params)
if user.save
render json: { message: "ユーザー登録が完了しました", user: {id: user.id, name: user.name, email: user.email} }, status: :created
else
render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
end
end
# プロフィール削除(退会処理)
def destroy
user = current_user
if user && user.destroy
render json: { message: "アカウントが削除されました。" }, status: :ok
else
render json: { error: "アカウントの削除に失敗しました" }, status: :unprocessable_entity
end
end
private
# ユーザー登録時のパラメータ
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
wrap_parameters :user, include: [:name, :email, :password, :password_confirmation]
end
end
end
Sessionsコントローラー
ユーザーのリグイン、ログアウトを管理するために、SessionsControllerを作成します
docker-compose exec web rails generate controller api/v1/Sessions
コントローラーを編集します
module Api
module V1
class SessionsController < ApiController
# ログイン
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
token = encode_token(user_id: user.id)
render json: { message: "ログインに成功しました。", token: token }, status: :ok
else
render json: { error: "メールアドレスまたはパスワードが無効です。" }, status: :unauthorized
end
end
# ログアウト
def destroy
render json: { message: "ログアウトしました。" }, status: :ok
end
private
# JWTトークンをエンコード
def encode_token(payload)
JWT.encode(payload, Rails.application.secrets.secret_key_base)
end
end
end
end
JWTの有効化
- Gemfileにjwtを追加
まず、プロジェクトのGemfileにjwtを追加します。
gem 'jwt'
- bundle installを実行
docker-compose run web bundle install
- build
docker-compose build
Postmanを使ってリクエスト
Postmanを使ってそれぞれリクエストを送ってみます。
新規登録
リクエストメソッドをPOSTに設定しURLを以下に設定する
http://localhost:3000/v1/signout
リクエストボディを以下のように設定して
{
"name": "bar",
"email": "bar@example.com",
"password": "password123"
}
リクエストを送ると以下のレスポンスが返ってきます。
{
"message": "ユーザー登録が完了しました",
"user": {
"id": 1,
"name": "bar",
"email": "bar@example.com"
}
}
ログイン
リクエストメソッドをPOSTに設定しURLを以下に設定する
http://localhost:3000/v1/login
リクエストボディを以下のように設定して
{
"email": "bar@example.com",
"password": "password123"
}
リクエストを送ると以下のレスポンスが返ってきます。
tokenはJWTでidをエンコードしたものです。
{
"message": "ログインに成功しました。",
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo1fQ.7wAvPaPWV1iQxks7yQhrFM5wJO6fDoiRvVbTHU7nxP0"
}
ログアウト
リクエストメソッドをDELETに設定しURLを以下に設定する
http://localhost:3000/v1/logout
リクエストボディは設定せずにリクエストを送ると以下のレスポンスが返ってきます。
{
"message": "ログアウトしました。"
}
ログアウトではフロント側でsessionnを削除する操作を行います。
退会
リクエストメソッドをDELETに設定しURLを以下に設定する
http://localhost:3000/v1/profile
リクエストヘッダにkayをAuthorization、valueをログインした時にトークンとして返ってきた値をセットします。
リクエストボディは設定せずに
リクエストを送ると以下のレスポンスが返ってきます。
tokenはJWTでidをエンコードしたものです。
{
"message": "アカウントが削除されました。"
}
まとめ
今回はRails APIでUser周りの実装をしました。ぜひ参考にしていただけたらと思います。
プログラムで気になったことを以下の記事にまとめたのでぜひご覧ください。
【Rails】Rails APIで新規登録、退会処理
【Rails】Rails APIでログイン、ログアウト