views:

818

answers:

3

I'm working with an application built in Ruby on Rails with very poor error handling right now. If a controller method is executed via ajax, and that method results in a 500 (or 404 or any other response) the 500.html page is rendered and returned as the result to the AJAX request. Obviously the javascript doesn't know what to do with that HTML and the web page looks like it's just waiting for a response.

Is there an easy way in rails to render an error.rjs template anytime an error occurs during an AJAX call?

+3  A: 

You can use respond_to inside a controller's rescue_action or rescue_action_in_public method. Consider the following controller:

class DefaultController < ApplicationController

  def index
    raise "FAIL"
  end

  def rescue_action(exception)
    respond_to do |format|
      format.html { render :text => "Rescued HTML" }
      format.js { render :action => "errors" }
    end
  end
end
cpm
+3  A: 

I solved a similar problem with authorization. I created a simple authorization controller with this action:

  def unauthorizedxhr
    render :update do |page|
      page.replace_html("notice", :partial=>"unauthorizedxhr")
      page.show("notice")       
    end
  end

Here's the template:

<% if flash[:notice] -%>
    <div id="noticexhr"><%= flash[:notice] %></div>
<% end -%>

When the authorization failed in the controller, I'd redirect to the :controller=>"authorization", :action=>"unauthorizedxhr" after setting the flash[:notice] value. This allowed me to customize the message I sent to the user, and it handled the display of the message through the render :update code above.

You could adapt this to your problem by creating an errors controller, catching any raised errors in your other controllers, then simply redirecting to :controller=>"errors", :action=>"displayxhr" when the call fails. That way, you'll have standardized your error communication mechanism but allowed yourself the ability to customize error messages by each action.

You can still use cpm's idea above, but the error's display will be handled by separate and distinct controller logic. that should make it a little easier to maintain.

Hope that helps. -Chris

Christopher Hazlett
+1  A: 

This was my final solution:

def rescue_action_in_public(exception)
  response_code = response_code_for_rescue(exception)
  status = interpret_status(response_code)
  respond_to do |format|
    format.html { render_optional_error_file response_code}
    format.js { render :update, :status => status  do |page| page.redirect_to(:url => error_page_url(status)) end}
  end
end

This basically forwards to the correct static HTML page no matter if the request was via AJAX or normal GET/POST.

This is NOT used for normal error handling like validation etc. It's only used when something really bad happens - like an unhandled exception.

Kyle Boon