# config/routes.rb
Rails.application.routes.draw do
# ... all your scopes and routes
match "(*any)", to: "application#render_not_found", via: :all
end
You can route unmatched routes individually within constrained contexts if desired
# config/routes.rb
Rails.application.routes.draw do
constraints subdomain: 'www' do
scope module: :web, as :web do
# .... routes
# www.example.com/bad_path routes to web/base_controller
match "(*any)", to: "base#render_not_found", via: :all
end
end
constraints subdomain: 'admin' do
scope module: :web, as :web do
# .... routes
# admin.example.com/bad_path routes to admin/base_controller
match "(*any)", to: "base#render_not_found", via: :all
end
end
# bad_subdomain.example.com/bad_path routes to application_conroller
match "(*any)", to: "application#render_not_found", via: :all
end
Desclare rescue_from
s in ApplicationController
, delegating to methods (via symbols).
That way rescue definitions will be inhereted by all our controllers, so they get exception handling for free but can override if desired by overriding the methods.
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
unless Rails.env.development?
# NOTE: these are rescued from bottom (most specific) to top (least specific)
rescue_from Exception, with: :render_exception
rescue_from Timeout::Error, with: :render_service_unavailable
rescue_from Rack::Timeout::Error, with: :render_service_unavailable
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
rescue_from ActionController::ParameterMissing, with: :render_bad_request
end
protected
def render_exception(exception, opts = {})
opts[:template] ||= "errors/internal_server_error"
opts[:status] ||= :internal_server_error
render_error(opts.merge(exception: exception))
end
def render_bad_request
render_error template: "errors/bad_request", status: :bad_request # 400
end
def render_forbidden
render_error template: "errors/forbidden", status: :forbidden # 403
end
def render_not_found
render_error template: "errors/not_found", status: :not_found # 404
end
def render_service_unavailable(exception, opts = {})
opts[:template] ||= "errors/service_unavailable"
opts[:status] ||= :service_unavailable
# The timeout may have occured during a
# persistence layer query; resetting the connection
# cancels the query so that a "closed connection" exception
# will not be raised during the next request
# that executes a query
ActiveRecord::Base.connection.reset!
render_error(opts.merge(exception: exception))
end
def render_unauthorized
render_error template: "errors/unauthorized", status: :unauthorized # 401
end
def render_error(opts = {})
# ErrorReporter.report(opts[:exception], request: request) if opts[:exception].present?
render opts[:template], status: opts[:status]
end
end
These methods render default or "fallback" views
# views/errors/not_found.html.haml
.error
.error-container
%h3 This page cannot be found!
%p
= link_to "Click here", root_path
to return to the home page.
# sytlesheets/web/_errors.scss
.error {
max-width: $site-width;
margin: 5% auto;
> .error-container {
text-align: center;
}
}
# sytlesheets/admin/_errors.scss
.error {
max-width: $site-width;
margin: 5% auto;
> .error-container {
font-weight: bold;
}
}
module Web
class BaseController < ApplicationController
private
def render_not_found
render_error template: "web/errors/not_found", status: :not_found # 404
end
end
end
# views/web/errors/not_found
%h2 404 - Here's a specific error message