45

With the default routing, the request /posts/:id gets mapped to the "show" action with :format => "html". I am using some xhtml elements in my show action which don't get rendered correctly unless the :content_type is set to xml. I am currently getting around this by rendering show.xml.erb and setting the content_type manually as follows:

format.html { render :template => "/posts/show.xml.erb", 
             :locals => {:post => @post}, :content_type => "text/xml" }

This seems silly though. How can I change routes.rb so that /posts/:id is routed with format=>"xml"? Thanks.

Saucerful
  • 619
  • 1
  • 6
  • 10

5 Answers5

96

Default format for requests:

You can set the default format of a given route to xml using the defaults hash.

Examples:

# single match defaulting to XML (/plots/1 is the same as /plots/1.xml)
match 'posts/:id' => 'posts#show', :defaults => { :format => 'xml' }

# using resources, defaulting to XML (all action use XML by default)
resources :posts, :defaults => { :format => 'xml' }

# using resources and mixing with other options
resources :posts,
          :only => [:new, :create, :destroy],
          :defaults => { :format => 'xml' }

It's always a good idea to search the official Ruby on Rails routing guide, it's fairly in-depth and a very good first-stop resource for any routing issues.

tomeduarte
  • 2,803
  • 1
  • 17
  • 10
  • 12
    You can also set the default format for a namespace and such like so: `namespace :user, :defaults => {:format => 'json'}` – Scott Fister May 20 '13 at 23:36
  • Awesome. Without the format set as above, I was getting 406 errors requesting xml even though everything else was cool in the controller. – Polsonby Nov 23 '14 at 16:36
26

If you only want to support one format and treat all requests as that format, you could use a filter to change it:

before_filter :set_format

def set_format
  request.format = 'xml'
end
Jimmy
  • 35,686
  • 13
  • 80
  • 98
  • 1
    Yes. If you choose to use this solution rather than the routes way (see my answer), you should set the filter as a private method, per security concerns. -- note that this forces the format as where the routes way does not, just sets default. – tomeduarte Jan 26 '11 at 00:31
15

Rails 4 and 5: In your controller (e.g. ApplicationController if all whole application uses same format) add following:

  before_action :set_default_request_format

  def set_default_request_format
    request.format = :json unless params[:format]
  end

For Rails 3 and older use before_filter instead of before_action.

Tombart
  • 30,520
  • 16
  • 123
  • 136
  • 1
    before_action should be used for rails 4 and going forward; rails 3 is before_filter: https://apidock.com/rails/v4.0.2/AbstractController/Callbacks/ClassMethods/before_action – Derk-Jan Mar 23 '18 at 15:17
5

If you want to set the default format for a route, use defaults option:

resources :posts, defaults: { format: 'xml' }

But if you want to enforce every request to return a specific format, use constraints option:

resources :posts, constraints: lambda { |req| req.format == 'xml' }

See the documentation: http://edgeguides.rubyonrails.org/routing.html#request-based-constraints

Son Dang
  • 341
  • 4
  • 5
  • 1
    The latter is what I came here looking for - quick note, I switched the lambda to use `==` and it worked perfectly, 404ing for other formats. – SRack May 31 '19 at 09:16
  • Thank you for pointing that out. I edited the answer following your suggestion. – Son Dang Jun 03 '19 at 15:46
4

I'm finding weird behaviour in Rails 5 if you use this:

{ format: :json }

In your config/routes.rb then even if JSON isn't set in your accept header, it still coerces the request to a JSON request, including for controller tests that have the as: :html option set. It's not really a big deal for me, so I'm not going to dig into why this is, but if someone figures it out, let me know and I'll update this answer.

zachaysan
  • 1,726
  • 16
  • 32