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.
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:
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.
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.
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.
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.
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.