2025年10月9日木曜日

Sinatra4.2.0にアップデートしたらホームページが動かなくなったので対応した

Sinatra4.2.0にアップデートしたらホームページが動かなくなったので対応した

私のホームページはSinatraでできています

背景

Sinatra が 4.2.0 になり PATH_INFO の扱いが変わりました https://github.com/sinatra/sinatra/compare/v4.1.1...f53a68482f3d5a1a868320f5da3ca7e14bfd8fdf

それに伴いルーティングの定義を仕方も変わったのでそれに対応しました

結論

ちゃんと get '/' do ... end を使いましょう

仕組み

自分のホームページは各ページをクラスとして定義しています
例えば /about を管理するページは以下のようにクラス化されています

# frozen_string_literal: true

require './libs/ogp'
require './controllers/base_page'

# 自己紹介ページのルーティングを定義するクラス
class AboutPage < BasePage
  def initialize
    @path = '/' # config.ru 側でパスを指定するのでここはすべて / になる
    @real_path = '/about' # sitemap 生成用のパス
    @tpl_name = :about
    @title = 'About @kakakikikeke'
    @description = '興味のある分野、職歴、資格について紹介しています'
    @image = OGP::DEFAULT_IMAGE
    @sitemap = { priority: 0.8 }
    super(@real_path, @sitemap)
    routing
  end

  def routing
    AboutPage.get @path do
      @ogp = OGP.new(@title, @description, @path, @image)
      erb @tpl_name
    end
  end
end

でこのクラスを config.ru で Rack::URLMap を使って読み込み

# frozen_string_literal: true

require './libs/routing'

run Rack::URLMap.new(Routing.definition)

展開しています

# frozen_string_literal: true

Dir[File.expand_path('../controllers', __dir__) << '/*.rb'].sort.each do |file|
  require file
end

# ルーティングを定義するモジュール、ちゃんとメニューの順番と同じになるようにrouting.jsonを定義すること
# また動的に生成するページは各Pageクラス内で定義すること
module Routing
  def self.definition
    JSON.parse(File.read('./routing.json'))['routing'].map do |info|
      [info['path'], Module.const_get(info['class']).new]
    end.to_h
  end
end

今回の現象

上記のように展開すると本来は 200 が返るのですがなぜか 404 になるようになってしまいました

対応策

で結果的に対応策は以下のようになりました
各ページで定義しているルーティング情報をメソッド内で呼び出すのではなくちゃんとクラス配下に定義してあげるようにしています

# frozen_string_literal: true

require './libs/ogp'
require './controllers/base_page'

# 自己紹介ページのルーティングを定義するクラス
class AboutPage < BasePage
  PATH = '' # config.ru 側でパスを指定するのでここはすべて空になる

  def initialize
    @real_path = '/about' # sitemap 生成用のパス
    @tpl_name = :about
    @title = 'About @kakakikikeke'
    @description = '興味のある分野、職歴、資格について紹介しています'
    @image = OGP::DEFAULT_IMAGE
    @sitemap = { priority: 0.8 }
    super(@real_path, @sitemap)
  end

  AboutPage.get PATH do
    @ogp = OGP.new(@title, @description, PATH, @image)
    erb @tpl_name
  end
end
  • @path を PATH として定数にする
  • かつ PATH は必ず空にする
  • routing メソッドを削除し get を直接クラス配下に定義する

これで一応動作するようになりました

おまけ: Rack::Lint::LintError: uppercase character in header name: Content-Security-Policy (Rack::Lint::LintError)

CSP ヘッダなどをミドルウェアで定義する場合は大文字を使ってはいけないようです

# frozen_string_literal: true

require './libs/routing'

# 静的ファイルに対するヘッダーを設定するミドルウェア
class StaticHeadersMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    path = env['PATH_INFO']
    if path =~ %r{^/(robots\.txt|favicon\.ico|img/|js/|css/|ipa/)}
      headers['content-security-policy'] ||= "default-src 'self';"
      headers['cross-origin-embedder-policy'] ||= 'require-corp'
      headers['cross-origin-opener-policy'] ||= 'same-origin'
      headers['cross-origin-resource-policy'] ||= 'same-origin'
      headers['permissions-policy'] = 'camera=(), microphone=(), geolocation=(), fullscreen=(self)'
    end

    [status, headers, body]
  end
end

use StaticHeadersMiddleware

run Rack::URLMap.new(Routing.definition)

最後に

メソッド内で get などを定義すると実際にアクセスされた際にルーティングの定義が見つからずエラーになるようです

0 件のコメント:

コメントを投稿