Railsアプリケーションでは、ルーティングで任意の条件により制約を設けることができます。 この記事では、その方法について解説します。
制御の種類
まず、アプリケーション側において、ユーザのリクエストに対する制御を行なう方法は、大別して以下の2つがあります。
1. ルーティングで制御する
ルーティングでの制御は、該当するリクエストからコントローラを隠蔽したいケースに適します。
例えば、接続元が特定のIP以外は/admin
の存在を隠蔽したい場合は、ルーティングで制御します。
2. コントローラで制御する
コントローラでの制御は、レコードの状態がリクエストを受け付けるのに相応しくないケースに適します。
例えば、認証済みのユーザに管理権限がない場合はコントローラ側で制御します。
備考
もちろん、ルーティングで行おうとする制御を、コントローラで行なうことはできます。 1の例でいうと、コントローラ側で接続元IPを確認し、特定のIP以外はRoutingErrorを発生させればよいでしょう。
ただ、制御自体はルーティング/コントローラどちらでもできますが、制御の一部であるルーティングはその名のとおりルーティングの責務です。
その制御が「どちらにふさわしいか」を判断し実装することで、よりきれいな設計にすることができます。
実装方法
それでは、具体的な実装方法について説明します。
まず、#matches?
というメソッドをもった制約クラスを作成します。
#matches?
の返り値がtrue
ならリクエストを受け付け、false
なら遮断します。
config/constraints/foo_constraint.rb
:
class FooConstraint
def matches?(request)
...
end
end
ルーティングでは、制約を設けたいresources
やscope
などで、:constraints
オプションに制約クラスのインスタンスを渡します。
config/routes.rb
:
resources :..., constraints: FooConstraint.new
あとは、config/constraints
ディレクトリを読み込むようautoload_paths
に追加します。
config/application.rb
:
config.autoload_paths += %W(
#{config.root}/config/constraints
)
実装例
ここでは、接続元が特定IPの場合のみ/admin
へのルーティングを受け入れる制約の例を示します。
config/constraints/whitelist_constraint.rb
:
class WhitelistConstraint
def initialize
@ips = Whitelist.ips
end
def matches?(request)
@ips.include?(request.remote_ip)
end
end
config/routes.rb
:
namespace :admin, constraints: WhitelistConstraint.new do
...
end
おわりに
ルーティングの制御を制約クラスに切り分けることで、ルーティングをきれいに実装できます。 適切なケースでは、ぜひ導入しましょう。