如何在Rails中设置路由的默认格式?

45

使用默认路由,请求/posts/:id将被映射到带有:format => "html"的"show"操作。 我在我的show操作中使用了一些xhtml元素,如果不将:content_type设置为xml,则无法正确呈现。我目前通过渲染show.xml.erb并手动设置content_type来解决此问题:

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

虽然这看起来很荒谬。但我该怎样修改routes.rb,使得/posts/:id以format=>"xml"的形式路由?谢谢。


你的问题对于模型来说有些混淆:你有图表还是帖子? - tomeduarte
抱歉,它是一个模型,称之为“posts”。 - Saucerful
5个回答

96

请求的默认格式:

你可以使用defaults hash将给定路由的默认格式设置为xml。

示例:

# 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' }

查看官方 Ruby on Rails 路由指南总是明智之举,该指南非常详尽,是任何路由问题的首选资源。


12
你还可以通过以下方式为命名空间设置默认格式:namespace :user, :defaults => {:format => 'json'}其中:defaults表示默认值,:format表示格式,此处设定为'json' - Scott Fister
太棒了。如果不按照上述格式设置,即使控制器中的其他内容都很好,我仍然会收到406错误请求xml。 - Polsonby

27
如果您只想支持一种格式,并将所有请求都视为该格式,那么您可以使用过滤器来更改它:
before_filter :set_format

def set_format
  request.format = 'xml'
end

1
是的。如果您选择使用此解决方案而不是路由方式(请参见我的答案),您应将过滤器设置为私有方法,以确保安全性。请注意,这种做法会强制指定格式,而路由方式则不会,只会设置默认值。 - tomeduarte

15

Rails 4和5:在您的控制器中(例如,如果整个应用程序使用相同的格式,则为ApplicationController),请添加以下内容:

  before_action :set_default_request_format

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

对于Rails 3及更早版本,请使用before_filter而不是before_action


1
before_action 应该用于 Rails 4 及以后版本;Rails 3 使用 before_filter:https://apidock.com/rails/v4.0.2/AbstractController/Callbacks/ClassMethods/before_action - Derk-Jan

5

如果你使用以下代码,在Rails 5中可能会遇到奇怪的行为:

{ format: :json }

在你的config/routes.rb文件中,即使accept头部没有设置JSON,它仍然会将请求转换为JSON请求,包括那些设置了as: :html选项的控制器测试。对我来说并不是很重要,所以我不会深入研究为什么会这样,但如果有人弄清楚了,就请告诉我,我会更新这个答案。


2
我也遇到了同样的问题。非常烦人。 - turboladen
我在Rails 6.1中也看到了这个问题。它会忽略发送到动作的任何“Accept”头部。它应该被命名为“:force”,因为它阻止使用任何其他请求类型。 - undefined

5
如果您想设置路由的默认格式,请使用defaults选项:
resources :posts, defaults: { format: 'xml' }

但是,如果您想强制每个请求返回特定格式,请使用 constraints 选项:
resources :posts, constraints: lambda { |req| req.format == 'xml' }

查看文档:http://edgeguides.rubyonrails.org/routing.html#request-based-constraints

1
后者是我在这里寻找的 - 快速记录一下,我将lambda切换为使用==,它完美地工作了,对于其他格式返回404。 - SRack
谢谢您指出这一点。我根据您的建议编辑了答案。 - Son Dang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接