ข้อผิดพลาดในการจัดการเพื่อประสบการณ์ผู้ใช้ที่ดีกว่าเป็นสิ่งที่ยากมากที่จะดึงออกมาอย่างถูกต้อง
ที่นี่ฉันได้จัดเตรียมเทมเพลตที่ครบถ้วนเพื่อให้ชีวิตของคุณง่ายขึ้น สิ่งนี้ดีกว่าอัญมณีเพราะสามารถปรับแต่งให้เข้ากับแอปพลิเคชันของคุณได้อย่างเต็มที่
หมายเหตุ: คุณสามารถดูเทมเพลตนี้เวอร์ชันล่าสุดได้ตลอดเวลาบนเว็บไซต์ของฉัน: https://westonganger.com/posts/how-to-properly-implement-error-exception-handling-for-your-rails-controllers
ตัวควบคุม
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
การทดสอบ
ในการทดสอบสิ่งนี้ในข้อกำหนดของคุณคุณสามารถใช้เทมเพลตต่อไปนี้:
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