【问题标题】:how to handle ActiveRecord::RecordNotFound in both ActionController::API and ActionController::Base如何在 ActionController::API 和 ActionController::Base 中处理 ActiveRecord::RecordNotFound
【发布时间】:2024-05-01 15:35:02
【问题描述】:

在这篇文章中,错误在 api 和基本控制器方法中都得到了解决。但它可能不是处理错误的最佳方法,原因如下:

  • 脂肪控制器
  • 干燥
  • 可维护性

在 ActionController::Base 中,我们只在 ApplicationController 中处理了 ActiveRecord::RecordNotFound。但是对于 ActionController::API 我必须在每个控制器中拯救 ActiveRecord::RecordNotFound 。那么有没有最好的方法来处理这个问题呢?

为 API 使用 Rails 5 和“active_model_serializers”gem

ActionController::API

module Api
  module V1
    class UsersController < ActionController::API
      before_action :find_user, only: :show    

      def find_user
        @user = User.find(params[:id])
      rescue ActiveRecord::RecordNotFound => e
        render json: { error: e.to_s }, status: :not_found
      end
    end
  end
end

ActionController::Base

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found


  private

  def record_not_found
    render file: "#{Rails.root}/public/404", layout: true, status: :not_found
  end
end

【问题讨论】:

  • @icemelt 这对我来说很有意义 :) 还有其他解决方案吗?

标签: ruby-on-rails activerecord active-model-serializers


【解决方案1】:

你可以在 application_controller.rb 中做这样的事情

if Rails.env.production?
  rescue_from ActiveRecord::RecordNotFound, with: :render_404
end

def render_404
  render json: {meta: meta_response(404, "Record not found")}
end

这将挽救所有带有 404 的 RecordNotFound 异常,但仅限于生产模式。

【讨论】:

    【解决方案2】:

    ActionController::API 包含ActionController::Rescue 模块,它提供了rescue_from 类方法。

    我会创建一个Api::V1::UsersController 可以使用的Api::BaseController 基类,而不是在每个控制器类上使用ActionController::API。这将允许您在一个地方有一个rescue_from,而不是在每个操作上都需要一个rescue 块。

    module Api
      class BaseController < ActionController::API
        rescue_from ActiveRecord::RecordNotFound, with: :handle_error
    
        private
    
        def handle_error(e)
          render json: { error: e.to_s }, status: :bad_request
        end
      end
    
      module V1
        class UsersController < BaseController
          def find_user
            @user = User.find(params[:id])
          end
        end
      end
    end
    

    我还将进一步创建一个Api::V1::BaseController 以允许更轻松地对 API 进行版本控制。然后,如果您决定更改 v2 的错误格式,只需将Api::BaseController 中的rescue_from 移动到Api::V1::BaseController,然后将新的rescue_from 添加到新的Api::V2::BaseController

    module Api
      class CommonBaseController < ActionController::API
        # code common across API versions
      end
    
      module V1
        class BaseController < CommonBaseController
          rescue_from ActiveRecord::RecordNotFound, with: :handle_error
    
          private
    
          def handle_error(e)
            render json: { error: e.to_s }, status: :bad_request
          end
        end
      end
    
      module V2
        class BaseController < CommonBaseController
          # use a custom base error class to make catching errors easier and more standardized
          rescue_from BaseError, with: :handle_error
    
          rescue_from ActiveRecord::RecordNotFound, with: :handle_error
    
          private
    
          def handle_error(e)
            status, status_code, code, title, detail =
              if e.is_a?(ActiveRecord::RecordNotFound)
                [:not_found, '404', '104', 'record not found', 'record not found']
              else
                [
                  e.respond_to?(:status) ? e.status : :bad_request,
                  e.respond_to?(:status_code) ? e.status_code.to_s : '400',
                  e.respond_to?(:code) ? e.code.to_s : '100',
                  e.respond_to?(:title) ? e.title : e.to_s,
                  e.respond_to?(:detail) ? e.detail : e.to_s
                ]
              end
    
            render(
              json: {
                status: status_code,
                code: code,
                title: title,
                detail: detail
              },
              status: status
            )
          end
        end
      end
    end
    

    【讨论】:

      最近更新 更多