The Pug Automatic

Show 404 instead of 500 for invalid format in Rails

Written September 3, 2008. Tagged Shell scripting, Rack.

When you visit /page?format=foo (and sometimes /page.foo, depending on the routes) in a Ruby on Rails app, Rails will try to render the action in that format - that is, using the page.foo.erb template.

If you use the

respond_to do |wants|
wants.html {}
wants.xml {}
end

syntax, requesting an invalid format gives you an empty "406 Not Acceptable" response.

Actions that do not use this syntax, though, will cause an ActionController::MissingTemplate exception – which the user sees as an unsightly "500 Internal Server Error".

That means actions that render implicitly like

def page
@page = Page.find(:first)
end

as well as explicitly like

def page
@page = Page.find(:first)
render :layout => 'example'
end

I don't want to show the 500 page unless necessary. In this case, a 404 page makes some sense; you could also argue for using a 406 error (for consistency with respond_to, if nothing else).

I added this to my ApplicationController:

# With +respond_to do |format|+, "406 Not Acceptable" is sent on invalid format.
# With a regular render (implicit or explicit), this exception is raised instead.
# Log it to Exception Logger, but show users a 404 page instead of error 500.
rescue_from(ActionController::MissingTemplate) do |e|
log_exception(e)
request.format = :html
render_404
end

The log_exception(e) line should obviously be changed or removed if you don't use Exception Logger, or don't want the errors logged.

render_404 was described in an earlier blog post. If you prefer an empty error 406, a simple

head(:not_acceptable)

should do it.

Note that none of this affects the 406 error when passing an invalid format to a respond_to action.