简体   繁体   中英

Store response HTTP status code after exception in Rails

I'm developing a REST web service in Ruby on Rails.

After each request I would like to store in the database the response HTTP status code even in presence of some exception. How can I do that?

I have done these two attempts without success:

  • after_filter in application controller

     class Api::ApiController < ActionController::Base before_action :set_current_rest_request after_filter :finalize_current_rest_request private def set_current_rest_request @current_rest_request = RestRequest.new @current_rest_request.request_at = DateTime.now @current_rest_request.save end def finalize_current_rest_request @current_rest_request.answer_at = DateTime.now @current_rest_request.http_status_code = response.status @current_rest_request.save end end 

    Doesn't work because finalize_current_rest_request is not called in case of exceptions

  • rescue_from in application controller

     class Api::ApiController < ActionController::Base before_action :set_current_rest_request after_filter :finalize_current_rest_request rescue_from Exception, :with => :handle_exception private def set_current_rest_request @current_rest_request = RestRequest.new @current_rest_request.request_at = DateTime.now @current_rest_request.save end def finalize_current_rest_request @current_rest_request.answer_at = DateTime.now @current_rest_request.http_status_code = response.status @current_rest_request.save end def handle_exception(exception) finalize_current_rest_request raise exception end end 

    Doesn't work because response.status is still 200 when I call finalize_current_rest_request inside handle_exception, before the raise of the exception

You need to wrap each action of the controller with a begin rescue block. Something like:

begin
  respond_to do |format|
    format.json { redirect_to foos_path, notice: 'Foo was successfully destroyed.' }
rescue 
  @current_rest_request.http_status_code = response.status
  end
end

Normally behavior like this is left up to the logs but I am assuming you have a really good reason for doing this.

If you really want it to be dry as you mentioned you can put in your application controller:

rescue_from Exception, :with => :store_request

def store_request
  current_rest_request = RestRequest.new
  current_rest_request.request_at = DateTime.now
  current_rest_request.http_status_code = response.status
  current_rest_request.save
end

Note: It is often considered bad practice to blanket rescue in the application controller. I think the best way to actually handle this is to implement a comprehensive logging scheme.

I finally solved adding a middleware ( here some datails on how rails middlewares work). In detail, in config/application.rb, I added:

config.middleware.insert_before "Rails::Rack::Logger","StoreStatus"

the api controller was

class Api::ApiController < ActionController::Base
    before_action :set_current_rest_request
    private
    def set_current_rest_request
      @current_rest_request = RestRequest.new
      request.env[:current_rest_request] = @current_rest_request
    end
end

and I added the file lib/store_status.rb with the following code:

class StoreStatus
  def initialize(app)
    @app = app
  end

  def call(env)
    data = @app.call(env)
    if (env[:current_rest_request])
      rest_request = env[:current_rest_request]
      rest_request.http_status_code = data[0]
      rest_request.save
    end
    data
  end
end

Please notice that there may be some syntax error because this code has been obtained refactoring a code which contains other useless complexity for this question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM