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
おわりに
ルーティングの制御を制約クラスに切り分けることで、ルーティングをきれいに実装できます。 適切なケースでは、ぜひ導入しましょう。