Active StorageのベースコントローラーにAPIオンリー対応の親クラス設定を追加

rails/rails

APIオンリーアプリ向けに、ActiveStorage::BaseControllerの親クラスをconfig.active_storage.base_controller_parentで設定できるようになりました。config.api_only = trueの場合は自動的にActionController::APIが使われるため、これまで回避策が必要だったCSRF検証エラーやOriginヘッダー不一致の問題が解消されます。

背景

APIオンリーのRailsアプリでActive Storageのダイレクトアップロードを使用すると、ActiveStorage::BaseControllerActionController::Baseを継承しているために問題が発生していました。#32208で報告された具体的なエラーは次の2点です:

  • HTTP Origin header didn't match request.base_url
  • Can't verify CSRF token authenticity.

これらはCSRF保護やオリジン検証を前提としたActionController::Base固有の仕組みに起因するものです。APIオンリーアプリのApplicationControllerActionController::APIを継承しているため、ActiveStorageコントローラーの親クラスと前提が食い違っていました。

これまでのワークアラウンドとしては、ActiveStorage::DirectUploadsControllerを継承したカスタムコントローラーを作成し、親クラスを差し替える方法が取られていましたが、Rails本体の設定として対応する公式な手段がありませんでした。

技術的な変更

ActiveStorage.base_controller_parentという新しいモジュール属性が追加され、ActiveStorage::BaseControllerはこの値を元に動的に親クラスを決定するようになりました。

activestorage/lib/active_storage.rbへの追加:

mattr_accessor :base_controller_parent, default: "::ActionController::Base"

activestorage/app/controllers/active_storage/base_controller.rbの変更:

# 変更前
class ActiveStorage::BaseController < ActionController::Base
  include ActiveStorage::SetCurrent

  protect_from_forgery with: :exception

  self.etag_with_template_digest = false
end
# 変更後
class ActiveStorage::BaseController < ActiveStorage.base_controller_parent.constantize
  include ActiveStorage::SetCurrent

  protect_from_forgery with: :exception if respond_to?(:protect_against_forgery?)

  self.etag_with_template_digest = false if respond_to?(:etag_with_template_digest)
end

親クラスを文字列として保持し.constantizeで解決する方式により、Engineの初期化順序に依存しない安全な定数解決が可能になっています。また、protect_from_forgeryetag_with_template_digestの呼び出しにrespond_to?ガードが追加され、ActionController::APIのように対応するメソッドを持たない親クラスでも安全に動作するようになっています。

activestorage/lib/active_storage/engine.rbでの自動設定:

ActiveStorage.base_controller_parent = app.config.active_storage.base_controller_parent ||
  if app.config.api_only
    "::ActionController::API"
  else
    "::ActionController::Base"
  end

Engineの初期化時にconfig.api_onlyの値を参照し、明示的な指定がない場合は自動的に適切な親クラスを選択します。ユーザーがconfig.active_storage.base_controller_parentを明示した場合はその値が優先されるため、ActionController::BaseActionController::API以外の独自の親クラスも指定できます。

activestorage/app/controllers/active_storage/disk_controller.rbの変更:

# 変更前
skip_forgery_protection
# 変更後
if respond_to?(:skip_forgery_protection)
  skip_forgery_protection
end

DiskControllerでも同様にrespond_to?ガードが追加されており、ActionController::APIを親クラスとする場合にNoMethodErrorが発生しないよう対処されています。

設計判断

クラス名を文字列として保持し.constantizeで解決する方式が採用されました。mattr_accessordefault値に文字列を使うことで、Engineがロードされる前の時点で定数参照が発生するのを避けています。

設定の優先順位は「明示的なbase_controller_parent指定 → api_onlyフラグによる自動選択 → ::ActionController::Base」の3段階になっています。これにより、既存の非APIアプリにはデフォルト値のまま何も変更せずに動作する後方互換性が確保されています。

respond_to?によるメソッド存在確認はコントローラー定義時(クラスボディ評価時)に実行されるため、ランタイムのオーバーヘッドはなく、親クラスの能力に応じた機能の有効化を静的に制御できる点が実用的な設計といえます。

まとめ

本PRは、APIオンリーアプリにおけるActive Storageの長年の課題を、Engineレベルの設定とコントローラーのrespond_to?ガードという最小限の変更で解決しています。既存アプリへの影響をゼロに保ちながら、config.api_onlyによる自動適用とbase_controller_parentによる手動上書きの両方を提供する設計は、Railsの設定哲学に沿った実装といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
b5f2e683

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という「総論→各論→結論」の構成が明確で、読者が理解しやすい構造になっています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)やGitHubのPR/Issueへのリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Railsの内部構造に関する専門的な内容であり、用語選択や説明のレベルが対象読者であるエンジニアに適切です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論・各論で構成され、各パラグラフがトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、高い可読性を実現しています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているすべてのコードブロックが、提供されたDiff情報と完全に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「mattr_accessor」「constantize」「Engine初期化」など、関連する技術用語が正確かつ適切な文脈で使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「.constantize」の採用理由や「respond_to?」ガードの役割など、コード変更の意図に関する技術的な説明が正確で論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのTitle, Description, Diffから裏付け可能であり、ハルシネーション(創作された情報)は検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#54189)や参照しているIssue番号(#32208)などの数値・固有名詞が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルはPRの主題「Allow configuring Active Storage base controller parent class」を的確に要約しており、内容との整合性が取れています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事の内容は提供されたPR情報に限定されており、バージョンのサポート状況やリリース日程といったPR外の外部知識を追記するようなことはありませんでした。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「〜できるようになりました」「これまで〜だった」といった時間表現が、変更の事実関係と一致しており、正確です。