Search code examples
ruby-on-railsrubyruby-on-rails-4routeshttp-status-code-422

Generating the Error Page for a 422 Error


I'm currently generating dynamic error pages for 500 and 404 errors. I want to expand this to 422 errors. Here's what we have so far.

config/application.rb

config.exceptions_app = self.routes

controllers/errors_controller.rb

class ErrorsController < ApplicationController
  def not_found
    render status: 404
  end

  def internal_server_error
    render status: 500
  end

  def unacceptable
    render status: 422
  end
end

routes.rb

get '/404' => 'errors#not_found'
get '/500' => 'errors#internal_server_error'
get '/422' => 'errors#unacceptable'

The public/422.html page has been deleted. The error view pages have been created but omitted for brevity. When a 404 or 500 error is raised, the error pages are shown. However, when I receive a 422 error I get the following error page.

enter image description here

I've seen numerous tutorials implementing this same approach and it works. However, I'm receiving a generated Rails error and not the error page I created. What's wrong and how do I fix this?

Tutorials I've looked at:


Solution

  • I'm another developer who worked on this with @jason328. It turned out to be a multi-part problem, first with general 422 errors, and then with the specific scenario in which Rails was raising ActiveRecord::InvalidAuthenticityToken and not rendering the appropriate page.

    1. General 422 Errors

    The Rails error page

    We got rid of this temporarily in our local development environment by setting config.consider_all_requests_local = false. But then instead of getting our custom error page, we got a blank white page.

    The blank white page

    As per this Stack Overflow question, we needed match '/422', to: 'errors#unprocessable_entity', via: :all for the route instead of get '/422' => 'errors#unprocessable_entity'.

    At this point, generic 422 errors performed as they should. We set up a controller action that raised ActiveRecord::InvalidAuthenticityToken as soon as you hit it, and it rendered our custom 422 page. So for anyone just having trouble with 422 errors in general, the above should cover you.

    2. InvalidAuthenticityToken

    But since a common cause of 422 errors is actually getting an InvalidAuthenticityToken error in the wild, it seems worth describing the rest of the problem we were seeing. In the actual scenario where the app was generating its own InvalidAuthenticityToken error, we were now getting a text-only 500 error, instead of our custom 422 page.

    Text-only 500 error

    We were able to trace this to the FAILSAFE_RESPONSE in ActionDispatch::ShowExceptions#render_exception. This is where Rails takes the exception that's been thrown and converts it to a [status, body, headers] response array. If another exception gets thrown during that time, rather than getting caught in an endless loop, it gives up and returns the FAILSAFE_RESPONSE. In this case, another InvalidAuthenticityToken error was getting thrown while putting together the response.

    At this point, it was time for the :rescue_from strategy:

    rescue_from ActionController::InvalidAuthenticityToken,
                with: :rescue_invalid_authenticity_token
    
    def rescue_invalid_authenticity_token
      #...notify services as if this error weren't being rescued
    
      redirect_to '/422'
    end
    

    with a redirect to keep us safe from any more InvalidAuthenticityToken errors in the same request.