Show 404 instead of 500 for invalid format in Rails

Written . Tagged Rack, Shell scripting.

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

1
2
3
4
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

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

as well as explicitly like

1
2
3
4
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:

1
2
3
4
5
6
7
8
# 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

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