ruby-on-railsrubyurlroutesname-collision

Rails: How to prevent routing clashes with static and dynamic pages?


I have the following route:

Rails.application.routes.draw do
  get '/:id', to: 'foo#bar', constraints: { id:  /\d+/ }
end

/1 loads my record with id #1. The problem is that my route conflicts with Rails default static pages (404, 500, etc ...).

How can I have static error pages with my dynamic routes working?

If it's possible, I don't mind moving my static pages to a route like /errors/404, for example.

EDIT 1:

Reopening ActionDispatch::ShowExceptions class and changing the private method render_exception is a extremely hacky solution:

config/application.rb:

require_relative 'boot'

require 'rails/all'

Bundler.require(*Rails.groups)

module MyApp
  class Application < Rails::Application
    config.load_defaults 5.2
    config.exceptions_app = self.routes
  end
end

module ActionDispatch
  class ShowExceptions
    private
      def render_exception(request, exception)
        backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner"
        wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
        status  = wrapper.status_code
        request.set_header "action_dispatch.exception", wrapper.exception
        request.set_header "action_dispatch.original_path", request.path_info
        request.path_info = "/errors/#{status}"
        response = @exceptions_app.call(request.env)
        response[1]["X-Cascade"] == "pass" ? pass_response(status) : response
      rescue Exception => failsafe_error
        $stderr.puts "Error during failsafe response: #{failsafe_error}\n  #{failsafe_error.backtrace * "\n  "}"
        FAILSAFE_RESPONSE
      end
  end
end

I've changed request.path_info = "/#{status}" to request.path_info = "/errors/#{status}".

I don't like this solution at all.


Solution

  • You can change the directory that renders exceptions. You will need to change some configuration, so for example you could put the following in config/application.rb within your Application class:

    config.exceptions_app = ActionDispatch::PublicExceptions.new(Rails.public_path.join('errors'))
    

    Then move those static 404.html and 500.html from public to a newly created public/errors dir.