君が代

基本的にワイの忘備録

【Vue忘備録】axiosを使ってAPIからデータを取得するの巻

前回の記事の続きです。

kimigayo.hatenablog.com

概要

今回作成しているのはタスク管理のサービスなので、タスクのモデルとコントローラーを作成して、タスクのデータをaxiosを使ってVueからAPIを呼び出す実装を行います。

Taskモデルとコントローラー

class Task < ApplicationRecord
  validates :title, presence: true
end
class Api::TasksController < ApplicationController
  before_action :set_task, only: %i[show update destroy]

  def index
    @tasks = Task.all
    render json: @tasks
  end

  def show
    render json: @task
  end

  def create
    @task = Task.new(task_params)

    if @task.save
      render json: @task
    else
      render json: @task.errors, status: :bad_request
    end
  end

  def update
    if @task.update(task_params)
      render json: @task
    else
      render json: @task.errors, status: :bad_request
    end
  end

  def destroy
    @task.destroy!
    render json: @task
  end

  private

  def set_task
    @task = Task.find(params[:id])
  end

  def task_params
    params.require(:task).permit(:title)
  end
end
Rails.application.routes.draw do
  root 'home#index'
  namespace :api do
    resources :tasks
  end
  get '*path', to: 'home#index'
end

タスクのデータはcurlコマンドを使って作成。 CSRFの対応を行わないとPOSTの処理ができないので下記も追加してください。

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
end

CSRFクロスサイトリクエストフォージェリ)とは

この攻撃方法は、ユーザーによる認証が完了したと考えられるWebアプリケーションのページに、悪意のあるコードやリンクを仕込むというものです。そのWebアプリケーションへのセッションがタイムアウトしていなければ、攻撃者は本来認証されていないはずのコマンドを実行できてしまいます。

railsguides.jp

RailsにはCSRFに対する機能が標準で用意されていますが、APIで開発する際にはバックエンドとフロントエンドが明確に分離しているのでクロスサイトの概念がなくなります。そのため protect_from_forgery with: :null_session を追加してCSRFトークンの検証を無効化します。

axiosの導入

yarnを使ってaxiosをインストールします。

yarn add axios

axiosはAPI通信をするためのHTTPクライアントです。 github.com

app/javascript/plugins/axios.js

import axios from 'axios'

const axiosInstance = axios.create({
  baseURL: 'api'
})

export default axiosInstance

baseURLについて

axiosを使うと通常は下記のような形式で記載します。

axios.get('https://localhost:3000/api/tasks')

ただ上記のものだと開発環境でしか使えないので本番環境に対応するには条件分岐する必要があります。

if(本番環境) {
  axios.get('https://kimigayo.com/api/tasks')
} else {
  axios.get('https://localhost:3000/api/tasks')
}

しかしこの書き方だとaxiosを使っている全ての場所で条件分岐を書く必要があるので、ここでbaseURLオプションを使ってaxiosに指定するURLからドメイン名を省略しています。

github.com

app/javascripts/packs/hello_vue.js

import Vue from 'vue'
import App from '../app.vue'
import router from '../router'
import axios from '../plugins/axios'
import 'bootstrap/dist/css/bootstrap.css'

Vue.config.productionTip = false
Vue.prototype.$axios = axios

import axios from '../plugins/axios' 先ほど作成したaxiosインスタンを読み込み

Vue.prototype.$axios = axios prototypeに入れる

prototypeに関して

ES6ではこのようにモジュールをインポートしてそれを使うという書き方をします。

import axios from 'axios' // インポートして
// 省略...
axios.get('/tasks') // それを使う

でも各コンポーネントでこのインポートの処理を書くのはアレなのでプロトタイプに入れることによって全てのVueインスタンスで使えるようにします。

$axiosのように$を入れてるのは定義したデータ、算出プロパティ、またはメソッドとの衝突を回避するための命名規則です。

このようにすることで全てのVueインスタンスthis.$axios.get〜 のような形でaxiosを使えるようになります。

jp.vuejs.org

APIの呼び出し

ここからは先ほど導入したaxiosを使ってAPIを呼び出していきます。

app/javascript/pages/task/index.vue

<script>
export default {
  name: "TaskIndex",
  data() {
    return {
      tasks: []
    }
  },
  created() {
    this.fetchTasks();
  },
  methods: {
    fetchTasks() {
      this.$axios.get("tasks")
        .then(res => this.tasks = res.data)
        .catch(err => console.log(err.status));
    }
  }
}
</script>

<style scoped>
</style>

まず初期表示でタスクを表示できないように、dataオブジェクトのtasksに空配列を定義します。

createdフック

次にライフサイクルフックのcreatedを使って、タスクデータの取得ロジックを書きます。 createdフックにはVueインスタンスが生成された後に実行したい処理を記述 します。 一般的にはこのcreatedフック内でtemplateで表示するためのデータを取得する処理を書きます。

jp.vuejs.org

メソッドの定義についてはmethodsに対して記述を行っています。

jp.vuejs.org

個人的な忘備録

リファクタリングする前は下記のような形で書いてた。

<script>
import axios from 'axios';

export default {
  name: "TaskIndex",
  data() {
    return {
      tasks: []
    }
  },
  mounted() {
    axios
      .get('/api/tasks.json')
      .then(response => (this.tasks = response.data))
  }
}
</script>

以上で終了!