Penanganan kesalahan untuk pengalaman pengguna yang lebih baik adalah hal yang sangat sulit untuk dilakukan dengan benar.
Disini saya telah menyediakan template yang lengkap untuk mempermudah hidup Anda. Ini lebih baik daripada permata karena sepenuhnya dapat disesuaikan dengan aplikasi Anda.
Catatan: Anda dapat melihat versi terbaru template ini kapan saja di situs web saya: https://westonganger.com/posts/how-to-properly-implement-error-exception-handling-for-your-rails-controllers
Kontroler
class ApplicationController < ActiveRecord::Base
def is_admin_path?
request.path.split("/").reject{|x| x.blank?}.first == 'admin'
end
private
def send_error_report(exception, sanitized_status_number)
val = true
return val
end
def get_exception_status_number(exception)
status_number = 500
error_classes_404 = [
ActiveRecord::RecordNotFound,
ActionController::RoutingError,
]
if error_classes_404.include?(exception.class)
if current_user
status_number = 500
else
status_number = 404
end
end
return status_number.to_i
end
def perform_error_redirect(exception, error_message:)
status_number = get_exception_status_number(exception)
if send_error_report(exception, status_number)
ExceptionNotifier.notify_exception(exception, data: {status: status_number})
end
logger.error exception
exception.backtrace.each do |line|
logger.error line
end
if Rails.env.development?
raise exception
end
if (request.format.html? && request.xhr?)
render template: "/errors/#{status_number}.html.erb", status: status_number
return
end
if status_number == 404
if request.format.html?
if request.get?
render template: "/errors/#{status_number}.html.erb", status: status_number
return
else
redirect_to "/#{status_number}"
end
else
head status_number
end
return
end
if request.referrer.present?
url = request.referrer
else
if current_user && is_admin_path? && request.path.gsub("/","") != admin_root_path.gsub("/","")
url = admin_root_path
elsif request.path != "/"
url = "/"
else
if request.format.html?
if request.get?
render template: "/errors/500.html.erb", status: 500
else
redirect_to "/500"
end
else
head 500
end
return
end
end
flash_message = error_message
if request.format.html?
redirect_to url, alert: flash_message
elsif request.format.js?
flash[:alert] = flash_message
flash.keep(:alert)
render js: "window.location = '#{url}';"
else
head status_number
end
end
rescue_from Exception do |exception|
perform_error_redirect(exception, error_message: I18n.t('errors.system.general'))
end
end
Menguji
Untuk mengujinya dalam spesifikasi Anda, Anda dapat menggunakan template berikut:
feature 'Error Handling', type: :controller do
controller(ApplicationController) do
def raise_500
raise Errors::InvalidBehaviour.new("foobar")
end
def raise_possible_404
raise ActiveRecord::RecordNotFound
end
end
before(:all) do
@user = User.first
@error_500 = I18n.t('errors.system.general')
@error_404 = I18n.t('errors.system.not_found')
end
after(:all) do
Rails.application.reload_routes!
end
before :each do
routes.draw do
get '/anonymous/raise_500'
get '/anonymous/raise_possible_404'
end
end
describe "General Errors" do
context "Request Format: 'html'" do
scenario 'xhr request' do
get :raise_500, format: :html, xhr: true
expect(response).to render_template('errors/500.html.erb')
end
scenario 'with referrer' do
path = "/foobar"
request.env["HTTP_REFERER"] = path
get :raise_500
expect(response).to redirect_to(path)
post :raise_500
expect(response).to redirect_to(path)
end
scenario 'admin sub page' do
sign_in @user
request.path_info = "/admin/foobar"
get :raise_500
expect(response).to redirect_to(admin_root_path)
post :raise_500
expect(response).to redirect_to(admin_root_path)
end
scenario "admin root" do
sign_in @user
request.path_info = "/admin"
get :raise_500
expect(response).to redirect_to("/")
post :raise_500
expect(response).to redirect_to("/")
end
scenario 'public sub-page' do
get :raise_500
expect(response).to redirect_to("/")
post :raise_500
expect(response).to redirect_to("/")
end
scenario 'public root' do
request.path_info = "/"
get :raise_500
expect(response).to render_template('errors/500.html.erb')
expect(response).to have_http_status(500)
post :raise_500
expect(response).to redirect_to("/500")
end
scenario '404 error' do
get :raise_possible_404
expect(response).to render_template('errors/404.html.erb')
expect(response).to have_http_status(404)
post :raise_possible_404
expect(response).to redirect_to('/404')
sign_in @user
get :raise_possible_404
expect(response).to redirect_to('/')
post :raise_possible_404
expect(response).to redirect_to('/')
end
end
context "Request Format: 'js'" do
render_views
scenario 'xhr request' do
get :raise_500, format: :js, xhr: true
expect(response.body).to include("window.location = '/';")
post :raise_500, format: :js, xhr: true
expect(response.body).to include("window.location = '/';")
end
scenario 'with referrer' do
path = "/foobar"
request.env["HTTP_REFERER"] = path
get :raise_500, format: :js
expect(response.body).to include("window.location = '#{path}';")
post :raise_500, format: :js
expect(response.body).to include("window.location = '#{path}';")
end
scenario 'admin sub page' do
sign_in @user
request.path_info = "/admin/foobar"
get :raise_500, format: :js
expect(response.body).to include("window.location = '#{admin_root_path}';")
post :raise_500, format: :js
expect(response.body).to include("window.location = '#{admin_root_path}';")
end
scenario "admin root" do
sign_in @user
request.path_info = "/admin"
get :raise_500, format: :js
expect(response.body).to include("window.location = '/';")
post :raise_500, format: :js
expect(response.body).to include("window.location = '/';")
end
scenario 'public page' do
get :raise_500, format: :js
expect(response.body).to include("window.location = '/';")
post :raise_500, format: :js
expect(response.body).to include("window.location = '/';")
end
scenario 'public root' do
request.path_info = "/"
get :raise_500, format: :js
expect(response).to have_http_status(500)
post :raise_500, format: :js
expect(response).to have_http_status(500)
end
scenario '404 error' do
get :raise_possible_404, format: :js
expect(response).to have_http_status(404)
post :raise_possible_404, format: :js
expect(response).to have_http_status(404)
sign_in @user
get :raise_possible_404, format: :js
expect(response).to have_http_status(200)
expect(response.body).to include("window.location = '/';")
post :raise_possible_404, format: :js
expect(response).to have_http_status(200)
expect(response.body).to include("window.location = '/';")
end
end
context "Other Request Format" do
scenario '500 error' do
get :raise_500, format: :json
expect(response).to have_http_status(500)
post :raise_500, format: :json
expect(response).to have_http_status(500)
end
scenario '404 error' do
get :raise_possible_404, format: :json
expect(response).to have_http_status(404)
post :raise_possible_404, format: :json
expect(response).to have_http_status(404)
sign_in @user
get :raise_possible_404, format: :json
expect(response).to have_http_status(500)
post :raise_possible_404, format: :json
expect(response).to have_http_status(500)
end
end
end
end