如何在ActionController :: API和ActionController :: Base中

时间:2017-05-31 07:12:58

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

在这篇文章中,错误在api和base控制器方法中得到了挽救。但由于某些原因,它可能不是处理错误的最佳方法:

  • 脂肪控制器
  • DRY
  • 可维护性

在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 ::基

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

2 个答案:

答案 0 :(得分:0)

您可以在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异常,但仅限于生产模式。

答案 1 :(得分:0)

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

我将创建一个Api::BaseController可以使用的Api::V1::UsersController基类,而不是在每个控制器类上使用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的错误格式,只需将rescue_from中的Api::BaseController移至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