10 Dec 2011
Make Rails 3 stop trying to serve HTML
Something kind of surreal happened today. I noticed that one of my Rails 3 apps was logging ActionView::MissingTemplate
errors. When I looked into it, the error was coming from an HTML template that didn’t exist. The problem was, that action wasn’t supposed to serve HTML at all, ever. I had even dutifully called clear_respond_to; respond_to :xml
in my controller, and I thought that would fix everything. Unfortunately, googling and checking Stack Overflow turned up nothing relevant to this particular version of the error, so I decided I had better just dig in.
The request was strange. It came from an IP address in China, and claimed to be asking for http://www.google.com/index.html
, even though the request was sent to my server’s IP. After some experimentation, I figured out that there was a problem with my routes: Rails 3 defaults to allowing any regular request to have its format specified with a file extension, like .html
. So even though I was responding with XML when the format wasn’t specified, my controller was trying to return HTML when it was explicitly requested.
The resource
routes allow you to supply a :format
parameter that sets the format for all requests to that resource. I thought that regular routes had a :format
parameter that worked the same way. It turns out they don’t. Regular routes (set by calling match
, get
, root
, and the like) will take :format => :xml
as an argument. But it turns out that argument is just a shortcut for :defaults => {:format => :xml}
. So while the format was XML if you didn’t ask for anything else, explicitly requesting HTML would still get you HTML.
The solution turned out to be adding :constraints => {:format => :xml}
to the route as well as setting the default format. That means that the routes you still want, like /index
and /index.xml
, will still work. Even better, it will reject requests for /index.html
as an invalid route, since the format no longer matches the constraint. Problem solved, but man that took more work than I was expecting to figure out.