以下の通りRails v7.2からブラウザのバージョン指定で利用をブロックできるallow_browserが追加されました。今回は個人のサービスで設定してみたのでメモ📝
2.2 Add browser version guard by default Rails now adds the ability to specify the browser versions that will be allowed to access all actions (or some, as limited by only: or except:). Ruby on Rails 7.2 Release Notes — Ruby on Rails Guides
使い方は簡単で以下の通り許可したいブラウザの最低バージョンを指定する感じになります。
class ApplicationController < ActionController::Base SUPPORT_BROWSER_VERSIONS = {safari: 14, firefox: 78, chrome: 87, edge: 88, opera: 106, ie: false} # NOTE: Viteのサポートブラウザー + RailsがサポートしてるOpreraを入れておく # https://vite.dev/guide/build#browser-compatibility # https://github.com/rails/rails/blob/v8.0.1/actionpack/lib/action_controller/metal/allow_browser.rb#L75 allow_browser versions: SUPPORT_BROWSER_VERSIONS
以下の通り、明示的に指定したブラウザに対して指定したバージョン未満の場合には、406 Not Acceptableでpublic/406-unsupported-browser.htmlを返却する挙動になります。
Only browsers matched in the hash or named set passed to versions: will be blocked if they’re below the versions specified. This means that all other browsers, as well as agents that aren’t reporting a user-agent header, will be allowed access. https://api.rubyonrails.org/v8.0.1/classes/ActionController/AllowBrowser/ClassMethods.html#method-i-allow_browser
実体の実装は以下のような感じなのでallow_browserにblockを渡してオーバーライドすれば、この辺りの返却するstatusやfile等は自由に設定できそうです。
def allow_browser(versions:, block: -> { render file: Rails.root.join("public/406-unsupported-browser.html"), layout: false, status: :not_acceptable }, **options) before_action -> { allow_browser(versions: versions, block: block) }, **options end
allow_browserで弾きたくはないけど、何かしら処理を分岐したい場合にはRailsのallow_browserでのサポートブラウザの判定処理はBrowserBlocker#blocked?で行われているので、以下のような感じでメソッドを定義してあげるとViewやその他の処理でサポートブラウザかどうか判定して分岐にも使えそうです。
class ApplicationController < ActionController::Base
SUPPORT_BROWSER_VERSIONS = {safari: 14, firefox: 78, chrome: 87, edge: 88, opera: 106, ie: false}
+ helper_method :from_support_browser?
+
+ private
+
+ def from_support_browser?
+ require "useragent"
+ !BrowserBlocker.new(request, versions: SUPPORT_BROWSER_VERSIONS).blocked?
+ end
以下のようなサポートブラウザかどうか判定用のCtonrollerを作ってAPI経由で呼び出して非同期でチェックするみたいなのもできそうですね 📝
class BrowserSupportController < ActionController::Api allow_browser versions: :modern, block: -> { head: :not_acceptable } def show; end
便利!