現在、APIを用いたタスク管理アプリを作成しているものです。
標記のとおりのエラーがでましたので、解決方法とエラーの原因について整理したいと思います。
#環境
Ruby 2.4.1
Ruby on Rails 5.2.2
Node.js 6.15.1
yarn 1.13.0
Webpacker 3.0.2
Vue.js 2.4.3
#ソースコード及びエラー内容
##ソースコード
controllers/api/tasks_controller.rb
class Api::TasksController < ApplicationController
# GET /tasks
def index
@tasks = Task.order('updated_at DESC')
end
# POST /tasks
def create
@task = Task.new(task_params)
if @task.save
render :show, status: :created
else
render json: @task.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /tasks/1
def update
@task = Task.find(params[:id])
if @task.update(task_params)
render :show, status: :ok
else
render json: @task.errors, status: :unprocessable_entity
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def task_params
params.fetch(:task, {}).permit(
:title, :important , :urgent
)
end
end
views/api/tasks/index.json.jbuilder
json.set! :tasks do
json.array! @tasks do |task|
json.extract! task, :id, :title, :important, :urgent
end
end
views/api/tasks/show.json.jbuilder
json.set! :task do
json.extract! @task, :id, :title, :important, :urgent
end
routes.rb
Rails.application.routes.draw do
root to: 'home#index'
namespace :api, format: 'json' do
resources :tasks, only: [:index, :create, :update]
end
end
##エラー内容
curlコマンドで新規APIの追加をしようとしたところ、だだだーっと意味不明なコードが流れました。
$ curl -X POST localhost:8080/api/tasks -d 'task[title]=fugafuga'(エラー)
ログをみてみるとこのような表示がされました。
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
有効ではないトークンみたいな感じでしょうか。
#解決方法
CSRFの対応について、rails使いが知っておくべきこと
RailsのCSRF対策について
外部からPOSTできない?RailsのCSRF対策をまとめてみた
を参考にさせていただきました。
ソースコードを以下のように変更します。
cotrollers/api/tasks_controller
class Api::TasksController < ApplicationController
protect_from_forgery #追記
(略)
結果、表示することができました!
$ curl -X POST localhost:8080/api/tasks -d 'task[title]=fugafuga'
{"task":{"id":11,"title":"fugafuga","important":null,"urgent":null}}
#なぜこのようなことが起きるのか?
railsはデフォルトでaplication.controller.rbにprotect_from_forgery
メソッドが定義されています。
これはCSRF(Cross-Site Request Forgery)の対策です。
CSRFは、簡単に言うと、悪意のあるユーザーがサーバーへのリクエストを捏造して正当なものに見せかけ、認証済みユーザーを装うという攻撃手法です。(詳しくは→RailsのCSRF保護を詳しく調べてみた(翻訳))
railsにおいては、ユーザーがPOSTリクエストをした際に、ページに埋め込まれているCSRFトークンを生成し、cookieにあるトークンを同一のものかを確認します。
railsは、get以外の動詞のリンクに、authenticity_tokenというパラメータを自動的に付け加えます。get以外の動詞の各アクションでparams[:authenticity_token]とsession[:csrf_id]を比較して、同値であればOKとしているようです。*1同値でなければActionController::InvalidAuthenticityTokenという例外がでます。
私の場合、なぜかprotect_from_forgery
が定義されていなかったので、新規で外部からPOSTしようとすると、エラーがでてしまいました。
今日のエラーは以上です。
なにか間違いがありましたらご指摘ください。