When routing resources in Rails the optional format attribute is automatically appended to the generated routes. This is so that the resource in question can be requested as either XML, HTML etc. Which formats that is actually allowed is usually described in the controller using respond_to.

But in many cases you only want to support HTML and it feels like an overhead to write respond_to :html in every action in every controller. It would therefore be cool if there where a way to limit to allowed content types already when building the routes in the routes.rb file, e.g.

map.resources :users, :formats => :html
map.resources :users, :formats => [:html, :xml]
map.resources :users, :formats => {:index => :html, :show => [:html, :xml]}

Is there a way to achieve this either native or via a plugin?

P.S. The usual way to work around this is to just ignore the problem and don't use respond_to in the actions. But this actually doesn't limit the allowed content types. Instead it just expects that a template exists in the views directory for each possible content type. If one doesn't exist when requested, the system will throw a HTTP 500 error.


You must wrap those routes in a scope if you want to restrict them to a specific format (e.g. html or json). Constraints unfortunately don't work as expected in this case.

This is an example of such a block...

scope :format => true, :constraints => { :format => 'json' } do
  get '/bar' => "bar#index_with_json"

More information can be found here: https://github.com/rails/rails/issues/5548

This answer is copied from my previous answer here..

Rails Routes - Limiting the available formats for a resource


Since Rails uses the equivalent of a wildcard to handle formats ".:format" it's a bit more difficult to prevent things on the route side.

Instead of this, it's pretty easy way to catch any non HTML requests in a before filter. Here's one way this might look:

class ApplicationController < ActionController::Base
  before_filter :check_format


    def check_format
      if request.format != Mime::HTML
        raise ActionController::RoutingError, "Format #{params[:format].inspect} not supported for #{request.path.inspect}"


ActionController::RoutingErrors are handled as 404 errors which is sensible. In the event that you do have an action that needs to support something other than HTML, just use:

skip_before_filter :check_format, :only => ACTION_NAME

I believe you are able to do something like this:

respond_to do |format|
  format.json { render :json => @things }
  format.any { render :text => "Invalid format", :status => 403 }

If the user requests html or json it'll do it as it should, but anything else will render the "Invalid Format" text.

  • that would work but i think he wants to drop the :format from the routes – user427165 May 16 '11 at 9:20

In either case wouldn't you want a HTTP 500 error? Like in the second line of your example, if someone requested JSON instead of HTML or XML isn't an error code return the appropriate response?

  • 1
    No, a 500 error means something went wrong on the server. If a content type is not supported it's not a server error - it's a client error (the client should not have requested it). A 406 would be the correct response code. See "HTTP Response codes": sitepoint.com/blogs/2008/02/04/restful-rails-part-i – Thomas Watson Sep 3 '09 at 16:58
  • Surely if you are going to tag .xml or .html on the end of the URL and that format is not supported then you should return 404 not found. It seem kind of cheeky to ignore the use of conneg by creating URLs for each content-type but then steal the repsonse code for when the there is no valid content type in the accept header! – Darrel Miller Sep 3 '09 at 18:31
  • Well you might have a point - even though this is actually how Rails works out of the box. But nevertheless this is not the point of my question. I would still like to specify this in a central location (preferably the routes file) - no matter if then a 404 or a 406 is returned on error – Thomas Watson Sep 3 '09 at 18:47
  • Setting aside what is the correct code to return. I think the point was that by convention it's doing what it ought to, what you are suggesting seems like it is configuration for the sake of configuration. – Mike Buckbee Sep 3 '09 at 19:48

rather than doing:

def some_action
  respond_to do |format|
    format.json { whatever }
    format.any { whatever  }

just use:

def some_action

and Rails will default to looking for some_action.html.erb or whatever format was requested. If you don't define any views other than html, then all other formats will fail if requested.


Your Answer

