How to set the default format for a route in Rails?
Asked Answered
D

5

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.

Didst answered 26/1, 2011 at 0:0 Comment(2)
your question is confusing about the model: do you have plots or posts ?Empoverish
oops, sorry. its one model, call it posts.Didst
E
99

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.

Empoverish answered 26/1, 2011 at 0:28 Comment(2)
You can also set the default format for a namespace and such like so: namespace :user, :defaults => {:format => 'json'}Ala
Awesome. Without the format set as above, I was getting 406 errors requesting xml even though everything else was cool in the controller.Cosma
M
27

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
Matronage answered 26/1, 2011 at 0:7 Comment(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.Empoverish
N
16

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.

Nealey answered 4/1, 2017 at 11:47 Comment(1)
before_action should be used for rails 4 and going forward; rails 3 is before_filter: apidock.com/rails/v4.0.2/AbstractController/Callbacks/…Caution
T
6

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

Tyler answered 19/11, 2017 at 0:53 Comment(2)
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.Hephaestus
Thank you for pointing that out. I edited the answer following your suggestion.Tyler
P
5

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.

Pale answered 24/7, 2017 at 8:59 Comment(2)
I'm seeing this same thing. Pretty annoying.Whisler
I see this in Rails 6.1 as well. It ignores any Accept header sent to the action. It should be named :force as it prevents any other request type from being used.Ventilate

© 2022 - 2024 — McMap. All rights reserved.