actionに対しての実行なのでそのとおりといえばそのとおりなんですが備忘として。
環境
$ bin/rails --version
Rails 6.0.4.1
TL;DR
- before_actionでredirect_toをするとafter_actionでは呼ばれない
- 回避案はいくつかあるが、action内でredirect_toをするなどすれば回避できる
今回考えるケース
以下のように必ず出力してほしいロギングを after_action
に設定し、特定の条件下でリダイレクトを行うようなケース
ケースに対してbefore_actionでredirect_toをし、after_actionでロギングする場合
コード
class RedirectTestsController < ApplicationController
before_action :redirect_check, only: :show
after_action :after_logging
def index
render json: { path: "index" }
end
def show
render json: { id: params[:id] }
end
private
def redirect_check
if params[:id].to_i == 10
logger.info("***** redirect *****")
redirect_to "/redirect_tests/"
end
end
def after_logging
logger.info("***** must after_logging! time: #{Time.zone.now} *****")
end
end
route.rbは以下
Rails.application.routes.draw do
resources :redirect_tests
end
このケースにおける問題点
上記のコードで http://localhost:3000/redirect_tests/1
にアクセスすると以下のようなログが出る。
Started GET "/redirect_tests/1" for ::1 at 2022-04-23 22:46:31 +0900
Processing by RedirectTestsController#show as HTML
Parameters: {"id"=>"1"}
***** must after_logging! time: 2022-04-23 13:46:31 UTC *****
Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 335)
そして http://localhost:3000/redirect_tests/10
にアクセスすると、以下のようなログが出る
Started GET "/redirect_tests/10" for ::1 at 2022-04-23 22:48:05 +0900
Processing by RedirectTestsController#show as HTML
Parameters: {"id"=>"10"}
***** redirect *****
Redirected to http://localhost:3000/redirect_tests/
Filter chain halted as :redirect_check rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.0ms | Allocations: 308)
Started GET "/redirect_tests/" for ::1 at 2022-04-23 22:48:05 +0900
Processing by RedirectTestsController#index as HTML
***** must after_logging! time: 2022-04-23 13:48:05 UTC *****
Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 317)
上記のようにbefore_actionでredirect処理が入ると、after_actionが実行されることがない。原理としては明確でactionの処理を実行することなく終わるため、actionが終わったあとに呼び出す after_action
が実行されない。
もちろんaround_actioのaction実行後の処理も呼び出されない。
改善案
いくつか方法論はありますが、before_actionとafter_actionの使い方をなるべく踏襲する形だと「レンダリング処理をコールバックでやるのを避ける」というのがあるので、今回はそちらを改善案として紹介します。
アプローチとコード
before_actionでリダイレクト処理を挟むとactionに到達する前に違うURLに行ってしまうので、遷移を挟む処理はbefore_actionでさせないようにする
class RedirectTestsController < ApplicationController
before_action :redirect_check, only: :show
after_action :after_logging
def index
render json: { path: "index" }
end
def show
redirect_to "/redirect_tests/" and return if @redirect_flag
render json: { id: params[:id] }
end
private
def redirect_check
@redirect_flag = false
if params[:id].to_i == 10
logger.info("***** redirect flag on *****")
@redirect_flag = true
end
end
def after_logging
logger.info("***** must after_logging! time: #{Time.zone.now} *****")
end
end
このやり方で http://localhost:3000/redirect_tests/10
にアクセスすると、以下のようなログが出て、ロギングがリダイレクト時にもちゃんとでる。
Started GET "/redirect_tests/10" for ::1 at 2022-04-23 22:58:14 +0900
(0.1ms) SELECT sqlite_version(*)
Processing by RedirectTestsController#show as HTML
Parameters: {"id"=>"10"}
***** redirect flag on *****
Redirected to http://localhost:3000/redirect_tests/
***** must after_logging! time: 2022-04-23 13:58:14 UTC *****
Completed 302 Found in 1ms (ActiveRecord: 0.0ms | Allocations: 512)
Started GET "/redirect_tests/" for ::1 at 2022-04-23 22:58:14 +0900
Processing by RedirectTestsController#index as HTML
***** must after_logging! time: 2022-04-23 13:58:14 UTC *****
Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 336)
参考リンク