VueとRailsを使ってログイン機能を実装する

結局フロントエンドをVue.js、バックエンドをRailsに任せるという構成にして、Vuexを導入することにした。

Vuexを導入した理由は、アプリ作成を進めていく中で画面数がプロトタイプよりも増えたから。階層が複雑になることによる弊害を回避したかった。

早速ログイン機能のロジックをVuexに置いて、ログイン画面(UserLogin.vue)でそのロジックを呼び出す流れの実装を進めた。

今回は頭の整理をするためにログイン時のWebAPI実行をまとめていきたい。

登場するファイル

必要なファイルは以下の通り。

  • src/plugins/axios/index.js
  • src/store/user.js
  • src/pages/UserLogin.vue
  • controllers/api/session_controller.rb

各ファイルの内容

src/plugins/axios/index.js

import axios from 'axios';

let csrf_token = document.getElementsByName('csrf-token')[0].content;
const axiosInstance = axios.create({
  baseURL: 'api',
  headers: { 'X-CSRF-TOKEN': csrf_token },
});

export default axiosInstance;

src/store/user.js

import axios from '../../plugins/axios'
import router from '../../router/index'
const state = {
    authUser: null,
};

const getters = {
    authUser: (state) => state.authUser,
};

const mutations = {
    setAuthUser(state, user){
        state.authUser = user;
    }
};

const actions = {
    loginUser({ commit }, user) {
        axios
          .post('session', user)
          .then((res) => {
              commit('updateAuthUser', res.data);
              router.push({ name: 'TopPage' });
              alert('ログインに成功しました')
          })
    }
};

export default {
    namespaced: true,
    state,
    getters,
    mutations,
    actions,

};

controllers/api/session_controller.rb

module Api

  class SessionsController < ApplicationController
    def create
      user=login(params[:email], params[:password])
      if user
        json_string = 
        UserSerializer.new(user).serializable_hash.to_json
        render json: json_string
      else
        head :unauthorized
      end 
    end
  end
end

src/pages/UserLogin.vue

<script>
import { mapActions } from 'vuex';
export default {
    data(){
        return{
            user: {
                email: '',
                password: '',
            },
            showPassword: false,
        };
    },
    methods: {
            ...mapActions('users', ['loginUser']),
            handleShowPassword(){
                this.showPassword = !this.showPassword;
            },
            handleLogin(){
                this.loginUser(this.user);
            },
    },
};
</script>

今回やりたい動作

登録したemailとpasswordをinputエリアに入力後ログインボタンを押す⇨トップページに遷移する。

ファイルの解説

API実行処理

実際にログイン部分を司っているのが、API処理を担うactionsになので、切り出してみた。

#src/store/module/user.js
const actions = {
    loginUser({ commit }, user) { #...1
        axios
          .post('session', user) #...2
          .then((res) => {
              commit('updateAuthUser', res.data); #...3
              router.push({ name: 'TopPage' }); #...4
              alert('ログインに成功しました')
          })
    }
};
  1. 第1引数のデータを第2引数に渡している。ちなみにmutationsの値を変えるためにcommitは存在している。
  2. axiosのpostアクション(createアクション)は第二引数にデータを渡す役割を持つ。
  3. 2をした後、アクションに成功したらupdateされたデータがres.dataに渡る。
  4. TopPageと命名されたコンポーネントに遷移させている。

コントローラーの挙動

2でpostアクションが実行されているが、sessions_controllerの中身を以下に記述しておく。

controllers/api/session_controller.rb

module Api

  class SessionsController < ApplicationController
    def create
      user=login(params[:email], params[:password])
      if user
        json_string = 
        UserSerializer.new(user).serializable_hash.to_json
        render json: json_string
      else
        head :unauthorized
      end 
    end
  end
end

sorceryのlogin(params[:email], params[:pasword])を使っている。

もしuserがtrueなら、レスポンス内容をjson形式に変換してクライアントに返却している。

jsonapi-serializerって何?

RailsAPIモードで開発をする際に、JSONを整形するためのgem

今回のAPI実行において、裏で行われている挙動は下記の通り。

  1. フロントがAPIサーバにリクエストを飛ばす。
  2. Webサーバはリクエスト内容に応じて、DBから値を持ってくる。
  3. そしてサーバはJSONAPIフォーマットでWebサーバに返す

いわゆるオーソドックスなHTTPリクエストとレスポンスのやりとりである。

この、3を行うためにjsonapi-serializerというものが存在しているみたいだ。

ログイン画面

src/pages/UserLogin.vue
<script>
import { mapActions } from 'vuex';
export default {
    data(){ #...1
        return{
            user: {
                email: '',
                password: '',
            },
            showPassword: false,
        };
    },
    methods: {
            ...mapActions('users', ['loginUser']), #...2
            handleShowPassword(){
                this.showPassword = !this.showPassword;
            },
            handleLogin(){ #...3
                this.loginUser(this.user);
            },
    },
};
</script>
  1. dataには今回使用しているuserデータを格納しておき、value(emailとpassword)もネストしてある。
  2. vuexに格納されているloginUserメソッドを、...mapActionsで呼び出している。importのようなものかと。
  3. 2で呼び出したloginUserメソッドをhandleLoginメソッドに擬態している。これで、クリックイベントが発火してhandleLoginが実行された時にloginUserと同じ挙動が行われるように出来た。

まとめ

  • ログイン機能を実装させることで、axiosによる非同期処理、vuexによる状態管理、jsonapi-serializer,mapActions,HTTPのやりとりに関してマルっと学べるのでいい勉強になる。

axiosライブラリを使ってリクエストする - Qiita

GitHub - jsonapi-serializer/jsonapi-serializer: A fast JSON:API serializer for Ruby (fork of Netflix/fast_jsonapi)

ステート | Vuex