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)