https://qiita.com/divclass123/items/fe54635b8434e9d82e08の続き。
前回は、動かすのに必要なコードを書いていったので、
理論上動くはずですが、実際に動くかどうか確かめたいと思います。
いいねボタンを押してみると。
ActionController::RoutingError (No route matches [POST] "/drinks/api/likes/10"):
なるほど。。。
const response = await axios.post('api/likes/' + drink.id)
namespace :api, { format: 'json' } do
resources :likes, only: [:index,:like,:unlike]
end
んー、なんで "/drinks/api/likes/10"
この処理が走ってるのか
const response = await axios.post('/api/likes/' + drink.id)
apiの前に/がなかったので付けてみる。
ActionController::RoutingError (No route matches [POST] "/api/likes/10"):
const response = await axios.post('/api/like/' + drink.id)
likesじゃなくて、likeですね。
ActionController::RoutingError (No route matches [POST] "/api/like/9"):
api_likes_index GET /api/likes/index(.:format) api/likes#index
api_likes_create GET /api/likes/create(.:format) api/likes#create
api_likes_destroy GET /api/likes/destroy(.:format) api/likes#destroy
const response = await axios.post('/api/likes/',{drink_id: drink.id})
今回の参考玉稿の通りにしてみる。
多分、パラメーターの理解が弱いから、
const response = await axios.post('/api/likes/',{drink_id: drink.id})
こういった書き方ができないんだな。
あとでrailsのパラメーターについて記事にします。
ActionController::RoutingError (No route matches [POST] "/api/likes"):
ダメだー。
like POST /like/:drink_id(.:format) likes#like
unlike DELETE /like/:drink_id(.:format) likes#unlike
railsの方のlikeはそもそも、こんな感じ。
api_likes_index GET /api/likes/index(.:format) api/likes#index
api_likes_create GET /api/likes/create(.:format) api/likes#create
api_likes_destroy GET /api/likes/destroy(.:format) api/likes#destroy
namespace :api, { format: 'json' } do
post '/api/like/:drink_id' ,to: 'api/likes#like', as: 'like'
delete '/api/like/:drink_id',to: 'api/likes#unlike', as: 'unlike'
end
こんな感じで書いてみる
したら
api GET /api/like/:drink_id(.:format) api/api/likes#index {:format=>/json/}
api_like POST /api/like/:drink_id(.:format) api/api/likes#like {:format=>/json/}
api_unlike DELETE /api/like/:drink_id(.:format) api/api/likes#unlike {:format=>/json/}
こんな感じに出力された。
namespaceをつけるとこうなるのね、って改めて感じた。
namespace :api, { format: 'json' } do
get 'like/:drink_id', to: 'likes#index'
post 'like/:drink_id' ,to: 'likes#like', as: 'like'
delete 'like/:drink_id',to: 'likes#unlike', as: 'unlike'
end
一旦こんな感じ。
api_drink GET /api/drinks/:id(.:format) api/drinks#show {:format=>/json/}
api_like POST /api/like/:drink_id(.:format) api/likes#like {:format=>/json/}
んー、なんでや。
namespace :api, { format: 'json' } do
resources :likes, only: [:index, :create, :destroy]
end
const response = await axios.post('/api/likes',{drink_id: drink.id})
で、likes_controllerをcreateとかにした。
そして、」こうなった。
うん。ちゃんとうまくいったが、
web_1 | Started POST "/api/likes" for 172.20.0.1 at 2021-08-20 17:42:02 +0000
web_1 | Cannot render console from 172.20.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
web_1 | Processing by Api::LikesController#create as JSON
web_1 | Parameters: {"drink_id"=>9, "like"=>{"drink_id"=>9}}
web_1 | Can't verify CSRF token authenticity.
web_1 | Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms | Allocations: 483)
Can't verify CSRF token authenticity.
なるほど、 CSRF 対策もしなきゃね。
それもhttps://qiita.com/TakeshiFukushima/items/a6c698fec648c11eee9a
この記事に書いてあったので、参考にします。
ってことで
yarn add axios rails-ujs
ただ、application.jsで
//= require rails-ujs
となぜかコメントアウトしてたので、そこらへんどうなんだろって感じ。
まぁとりあえず。
import { csrfToken } from 'rails-ujs'
// CSRFトークンの取得とリクエストヘッダへの設定をしてくれます
axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken()
こんな感じの記述
rails-ujs.js:694 Uncaught Error: If you load both jquery_ujs and rails-ujs, use rails-ujs only.
こんな感じでエラーがでた。
jquery_ujsをvscodeで検索したがヒットしなかった。
jqueryで思い出すのが以前背景画像をスライドさせたい時にjqueryを使った。
application.js
require("@rails/ujs").start()
// require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
// require("../bgswitcher")
require("../card")
require("./tag")
// require("../slide")
require('./preview')
//= require rails-ujs
でjqueryに関連しそうなものを一旦コメントアウト
vueとjqueryが背反する感じなら割と困る。
でもコメントアウトしても変わらず。
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
と記述することで解決するらしい。どういった原理かもあとで調べる必要ありそう。
なんか知らんけど解決できました系駆け出しエンジニアから卒業したい。
Error in v-on handler (Promise/async): "Error: Request failed with status code 500"
とブラウザではエラーが出た。
web_1 | Can't verify CSRF token authenticity.
web_1 | Drink Load (0.7ms) SELECT `drinks`.* FROM `drinks` WHERE `drinks`.`id` = 10 LIMIT 1
web_1 | ↳ app/controllers/api/likes_controller.rb:30:in `set_variables'
web_1 | Completed 500 Internal Server Error in 8ms (ActiveRecord: 0.7ms | Allocations: 1649)
web_1 |
web_1 |
web_1 |
web_1 | NoMethodError (undefined method `likes' for nil:NilClass):
bundig.pryでもありましたが、current_userがnilになっていました。
なるほど、どうやってカレントユーザーを定義していたのか。
# 現在ログインしているユーザーの情報を取得
def current_user
# DBの問い合わせの数を可能な限り小さくしたい
# logged_in?メソッドでも使われてるし、、、
if user_id = session[:user_id]
# セッションがある場合
# すなわちログインしてる時のみ
# sessionにアクセスした結果を変数に
# 入れておいてあとで使いまわした方が
# 早くなる
@current_user ||= User.find_by(id: user_id)
# find_byでデータベースにクエリを投げる
# ブラウザのセッションにあるuser_idをもとにUser定義
# find_byの実行結果をインスタンス変数に保存する
# ことで、1リクエスト内におけるデータベースへの
# 問い合わせは最初の一回だけになり、
# 以後の呼び出しではインスタンス変数の結果を
# 再利用する
# すでに@current_userが存在する場合って何?
# 一回current_userを実行したら、
# @current_userがあるのでそれを使ってね
# sessionのuser_idがあるということは
# 既にログインしてるといてDBにユーザーの情報があるはず。
# だからsessionのuser_idをDBでfind_byかければいい
elsif (user_id = cookies.signed[:user_id])
# sessionが張られてなかったらcookiesにあるかも
user = User.find_by(id: user_id)
if user&.authenticated?(:remember, cookies[:remember_token])
# nilガード
# クッキーのuser_idとremember_tokenが一致してる
log_in user
@current_user = user
end
end
end
基本的には、 User.find_by(id: user_id)で取りにいってる。
NameError: undefined local variable or method `user_id' for #Api::LikesController:0x007f9443f5a378
とエラーが出た。
ってことで、
パラメーターでuser_idを渡してしまえばいいんじゃないかってことで、
const response = await axios.post('/api/likes',{drink_id: drink.id,user_id: user.id})
こんな感じに記述。
したら
[1] pry(#<Api::LikesController>)> params
=> #<ActionController::Parameters {"drink_id"=>7, "user_id"=>7, "format"=>"json", "controller"=>"api/likes", "action"=>"create", "like"=>{"user_id"=>7, "drink_id"=>7}} permitted: false>
こんな感じになったので、
def set_variables
+ @user = User.find(params[:user_id])
@drink = Drink.find(params[:drink_id])
end
と記述。
def create
binding.pry
@like = @user.likes.new(drink_id: @drink.id)
@like.save
それに伴いこんな感じで記述。
web_1 | Processing by Api::LikesController#create as JSON
web_1 | Parameters: {"drink_id"=>7, "user_id"=>7, "like"=>{"user_id"=>7, "drink_id"=>7}}
web_1 | Can't verify CSRF token authenticity.
web_1 | User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 7 LIMIT 1
web_1 | ↳ app/controllers/api/likes_controller.rb:29:in `set_variables'
web_1 | Drink Load (0.5ms) SELECT `drinks`.* FROM `drinks` WHERE `drinks`.`id` = 7 LIMIT 1
web_1 | ↳ app/controllers/api/likes_controller.rb:30:in `set_variables'
web_1 | CACHE Drink Load (0.0ms) SELECT `drinks`.* FROM `drinks` WHERE `drinks`.`id` = 7 LIMIT 1
web_1 | ↳ app/controllers/api/likes_controller.rb:13:in `create'
web_1 | TRANSACTION (0.3ms) BEGIN
web_1 | ↳ app/controllers/api/likes_controller.rb:13:in `create'
web_1 | Like Create (0.9ms) INSERT INTO `likes` (`user_id`, `drink_id`, `created_at`, `updated_at`) VALUES (7, 7, '2021-08-21 05:43:55.461675', '2021-08-21 05:43:55.461675')
web_1 | ↳ app/controllers/api/likes_controller.rb:13:in `create'
web_1 | Drink Update All (0.6ms) UPDATE `drinks` SET `drinks`.`likes_count` = COALESCE(`drinks`.`likes_count`, 0) + 1 WHERE `drinks`.`id` = 7
web_1 | ↳ app/controllers/api/likes_controller.rb:13:in `create'
web_1 | TRANSACTION (2.0ms) COMMIT
web_1 | ↳ app/controllers/api/likes_controller.rb:13:in `create'
web_1 | No template found for Api::LikesController#create, rendering head :no_content
Can't verify CSRF token authenticity.
前回CSRF対策を記述したはずだが、なぜかまだある。
No template found for Api::LikesController#create, rendering head :no_content
createアクションは動いてるっぽいが、こんなエラーが出ていた。
Error in v-on handler (Promise/async): "TypeError: process.exit is not a function"
ブラウザのエラーはこんな感じ。
おそらく、
def create
@like = @user.likes.new(drink_id: @drink.id)
@like.save
# redirect_to drinks_path
# jsを用いるので画面遷移は行わない
# binding.pry
#=> like.js.erbに遷移する。
head :created
end
head :created
の部分を書いてなくて、ステータスコードを返してなかったから、動かなかったと思う。
またいいねボタンを押すと
web_1 | ↳ app/controllers/api/likes_controller.rb:13:in `create'
web_1 | Completed 201 Created in 33ms (ActiveRecord: 6.2ms | Allocations: 6130)
web_1 |
web_1 |
web_1 | Started GET "/api/likes/?drink_id=9" for 172.20.0.1 at 2021-08-21 05:53:19 +0000
web_1 | Cannot render console from 172.20.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
web_1 | Processing by Api::LikesController#index as JSON
web_1 | Parameters: {"drink_id"=>"9"}
web_1 | Completed 404 Not Found in 1ms (ActiveRecord: 0.0ms | Allocations: 706)
web_1 |
web_1 |
web_1 |
web_1 | ActiveRecord::RecordNotFound (Couldn't find User without an ID):
web_1 |
web_1 | app/controllers/api/likes_controller.rb:31:in `set_variables'
fetchLikeByDrinkId関数の
const response = await axios.get(`/api/likes/?drink_id=${drink.id}`)
のuser_id をパラメーターで渡してないから、
def set_variables
@user = User.find(params[:user_id])
@drink = Drink.find(params[:drink_id])
end
の
@user = User.find(params[:user_id])
でエラーが出る。
const response = await axios.get(`/api/likes/?drink_id=${drink.id}`,{user_id: user.id})
こんな感じで設定してあげよう。
しかしなぜか、couldnt idやらなんやら
pry(#<Api::LikesController>)> params
=> #<ActionController::Parameters {"drink_id"=>10, "user_id"=>7, "format"=>"json", "controller"=>"api/likes", "action"=>"create", "like"=>{"user_id"=>7, "drink_id"=>10}} permitted: false>
[2] pry(#<Api::LikesController>)> @drink = Drink.find(params[:drink_id])
Drink Load (0.7ms) SELECT `drinks`.* FROM `drinks` WHERE `drinks`.`id` = 10 LIMIT 1
↳ (pry):8:in `set_variables'
=> #<Drink:0x00007f91a69c4818
id: 10,
name: "ddd",
price: 3000,
explain: "dddddd",
user_id: 7,
created_at: Fri, 06 Aug 2021 06:50:21.611875000 UTC +00:00,
updated_at: Fri, 06 Aug 2021 06:50:21.649513000 UTC +00:00,
region_id: 1,
body_id: 1,
acidity_id: 1,
processing_id: 1,
likes_count: 6>
[3] pry(#<Api::LikesController>)> @user = User.find(params[:user_id])
User Load (0.7ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 7 LIMIT 1
↳ (pry):9:in `set_variables'
=> #<User:0x00007f91a6a0d4a0
id: 7,
nickname: "ゲスト様",
email: "guest@example.com",
created_at: Sat, 12 Jun 2021 13:36:24.072977000 UTC +00:00,
updated_at: Fri, 06 Aug 2021 06:50:39.628019000 UTC +00:00,
password_digest: [FILTERED],
remember_digest: nil,
activation_digest: "$2a$12$/F4yyBbZsUGt49w9SIbrr.e.el7GOIzoRsJHJGbPeNaUkHmTFDAOy",
activated: false,
activated_at: nil>
多分、
registerLike: async function(){
// rails側のcreateアクションにリクエストするメソッド
const response = await axios.post('/api/likes',{drink_id: drink.id,user_id: user.id})
こっちはうまく動いてるけど、
fetchLikeByDrinkId: async function(){
// async function()
// jsの非同期処理
const response = await axios.get(`/api/likes/?drink_id=${drink.id}`,{user_id: user.id})
こっちはうまくパラメーターを渡せていない。
fetchLikeByDrinkId: async function(){
// async function()
// jsの非同期処理
const response = await axios.get('/api/likes',{drink_id: drink_id,user_id: user.id})
ってことでパラメーターの渡し方をこんな感じに。
それでも
web_1 | ActiveRecord::RecordNotFound (Couldn't find User without an ID):
web_1 |
web_1 | app/controllers/api/likes_controller.rb:33:in `set_variables'
だったので調べてみると
const response = await axios.get('/api/likes',{params: {drink_id:drink.id,user_id: user.id}})
getの場合はこんな感じでパラメーターを渡してあげるみたい。
これで理論上動くはず。
params
=> #<ActionController::Parameters {"drink_id"=>"10", "user_id"=>"7", "format"=>"json", "controller"=>"api/likes", "action"=>"index"} permitted: false>
[2] pry(#<Api::LikesController>)> @like = Like.filter_by_drink(params[:drink_id]).select(:id, :user_id, :drink_id)
=> Like Load (1.2ms) SELECT `likes`.`id`, `likes`.`user_id`, `likes`.`drink_id` FROM `likes` WHERE `likes`.`drink_id` = 10
↳ app/controllers/api/likes_controller.rb:7:in `index'
[#<Like:0x00007f91a61de0b8 id: 43, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61ddf28 id: 44, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61ddde8 id: 45, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61ddca8 id: 46, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61ddb68 id: 47, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61dd708 id: 48, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61dd488 id: 49, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61dd190 id: 50, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61dcee8 id: 51, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61dccb8 id: 52, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61dc948 id: 53, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61dc060 id: 54, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61e3e50 id: 55, user_id: 7, drink_id: 10>,
#<Like:0x00007f91a61e3d38 id: 56, user_id: 7, drink_id: 10>]
サーバーサイドの方は問題ないが
hello_vue-f8e75860e88b4e423bd0.js:sourcemap:5938 TypeError: this.fetchLikeByPostId is not a function
とブラウザでエラーが起きてる
def index
# その投稿のいいね一覧を取得
render json: Like.filter_by_drink(params[:drink_id]).select(:id, :user_id, :drink_id)
end
データをjson形式で返せてるか怪しいので、
一旦、jbuilderに頼らずに、こんな感じの記述
likeButton.vue:68 Uncaught (in promise) ReferenceError: likthis is not defined
謎のタイポも発見
this.likeListにしたい。
なんとか、createは動いたが、今度はデリートが動いてない。
ActiveRecord::RecordNotFound (Couldn't find User without an ID):
app/controllers/api/likes_controller.rb:33:in `set_variables'
const response = await axios.delete("/api/likes",{drink_id: drink.id,user_id: user.id})
こんな感じで指定。
ActionController::RoutingError (No route matches [DELETE] "/api/likes"):
今度はこうなった。
const response = await axios.delete(`/api/likes/${likeId}`,{params: {drink_id:drink.id,user_id: user.id}})
こうやって設定!!
したらうまくいった!!!
やっといいね機能が実装できた!!!!
{params: {drink_id:drink.id,user_id: user.id}}
が何個もあるのでリファクタリングしたいが
下手に動かすとめんどいので一旦これでok!!!!