Axios + RailsでのPOSTの非同期化

What

Railsにおいて、GET以外のメソッド (POST, UPDATE, DELETE) を非同期で扱う際は、CSRF対策が施されています。

本記事は、非同期処理とは何か、そしてまた何であったかを知るための備忘録です。

下準備

ルーティング、コントローラー、モデル等を作成しておきます。

Rails.application.routes.draw do
  root 'main#index'
  post '/post', to: 'main#create'
end
class MainController < ApplicationController
  def index
  end

  def create
    post = Post.create(post_params)
  end

  private
  def post_params
    params.permit(:title,:content)
  end
end
class Post < ApplicationRecord
end
class CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
      t.string :title
      t.text :content
      t.timestamps
    end
  end
end

view側は次のようにしておきます。Slimで書いています (cssとかは触りたくなかったため、brを多用していて汚い)。
ちなみに、formでsubmitを囲っておかないと、うまく発火しません。

# views/main/index.html.slim

form[id='myForm']
  | title:
  input[type='text' name='title' value='initial post']
  br
  br
  | content:
  input[type='text' name='content' value='hello world wide web!!!']
  br
  input[type='submit' value='submit data']

gemで非同期処理をする

jQueryRailsにおいて、gemとして利用することができます。
gemとして利用した場合は、CSRF対策が施された状態なので、普通にPOSTなどができます。

$(function(){
  $('#myForm').submit(function(e){
    e.preventDefault();

    var myData = {
      title: $('input[name="title"]').val(),
      content: $('input[name="content"]').val()
    }

    $.ajax({
      url: '/post',
      type: 'post',
      data: myData,
      success: function(data){
        console.log(data)
      },
      error: function(){
        console.log('error!!!')
      }
    })
  })
})

$.ajaxの代わりに以下で書いても動きます。

    var url='/post'
    $.post(url, myData).done(function(data){
      console.log(data);
    })

上記は、/postに向けてPOSTメソッドを実行しています。
まず、ルーティングでmainコントローラーのcreateアクションが実行されます。
そのアクションで、paramsで保存された値をDBへ追加します。
Sequel Proでレコードを確認すると、きちんと保存できていることがわかります。

Axiosの場合

同じように書いてやります。
ここでは、getElementByIdが認識されなかったため、jQueryのメソッドですが、$(function(){})で囲って対処しています。
以下の書き方では、CSRF対策に引っかかり、POSTメソッドが失敗します。

$(function(){
  var myForm = document.getElementById('myForm');
  myForm.addEventListener('submit', function(e){
    e.preventDefault();
    var url = '/post'
    var myData = {
      title: document.querySelector('input[name="title"]').value,
      content: document.querySelector('input[name="content"]').value
    }
    axios.post(url,myData)
    .then(function(data){
      console.log(data)
    })
  })
})

以下のように、CSRFトークンを付け加える設定をすれば、AxiosでもPOSTメソッドの非同期化ができます。

$(function(){
  // import axios from 'axios'
  axios.defaults.headers.common = {
    'X-Requested-With': 'XMLHttpRequest',
    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
  };

  var myForm = document.getElementById('myForm');
  myForm.addEventListener('submit', function(e){
    e.preventDefault();
    var url = '/post'
    var myData = {
      title: document.querySelector('input[name="title"]').value,
      content: document.querySelector('input[name="content"]').value
    }
    axios.post(url,myData)
    .then(function(data){
      console.log(data)
    })
  })
})