限制Rails渲染格式为HTML的方法

13
我有一个Rails 2.1.2网站,只有html模板,例如jobs.html.erb,因此当我请求一个restful资源时:

www.mysite.com/jobs/1

它将以html形式呈现我的工作,但是,如果我请求:

www.mysite.com/jobs/1.xml

我会得到错误:

Template is missing Missing template jobs/show.xml.erb in view path c:/workspace/mysite/app/views

更糟糕的是,我还可以请求类似于

www.mysite.com/jobs/1.xyz

然后我会看到错误:

Template is missing Missing template jobs/show.xyz.erb in view path c:/workspace/mysite/app/views

为了严格呈现只有html内容,请问最干净和简单的方法是告诉Rails我不想呈现除.html.erb文件以外的任何东西。
需要注意的是:
  • 我的一些控制器操作包含对render()方法的条件调用,而其他操作使用默认的Rails行为,即如果您不调用render(),则将呈现名为youraction.html.erb的模板。
  • 我的代码不使用responds_to()方法
如果解决方案不在render/responds_to级别,那将非常好,因为我必须修改大量操作。也许有一种方法可以配置Rails,以便仅呈现html模板?
5个回答

14

在你的路由中,你可以简单地删除这一行:

map.connect ':controller/:action/:id.:format'

".xyz"将不再路由,导致404错误。


删除这行代码并不能解决Rails 3.2.6中的问题:它已经被注释掉了,但“缺少模板”错误仍然出现。 - collimarco

10
你可以使用Rails的Per-Action Overwrite功能。这是什么?-->还可以通过传递块到respond_with来覆盖标准资源处理,指定要为该操作覆盖哪些格式。
class UsersController < ApplicationController::Base

  respond_to :html, :xml, :json

  # Override html format since we want to redirect to a different page,
  # not just serve back the new resource
  def create
    @user = User.create(params[:user])
    respond_with(@user) do |format|
      format.html { redirect_to users_path }
    end
  end
end

:except和:only选项

你也可以传递:except:only选项来仅针对特定操作支持格式(就像使用before_filter一样):

class UsersController < ApplicationController::Base
  respond_to :html, :only => :index
  respond_to :xml, :json, :except => :show
  ...
end

:any格式

如果您仍然希望在单个操作中使用respond_to,请使用:any资源格式,它可以用作与任何未指定格式的通配符匹配:

class UsersController < ApplicationController::Base

  def index

    @users = User.all

    respond_to do |format|
      format.html
      format.any(:xml, :json) { render request.format.to_sym => @users }
    end
  end
end

Jared,请问你能具体说明一下这三个选项中哪一个是仅适用于Rails 3吗?我正在使用Rails 2.3.4,而且:any选项可以正常工作。 - leviathan

7
如果您不想使用responds_to,可以这样做:

如果不想使用responds_to,可以这样做:

class ApplicationController < ActionController::Base
  before_filter :allow_only_html_requests

  ...

  def allow_only_html_requests
    if params[:format] && params[:format] != "html"
      render :file => "#{RAILS_ROOT}/public/404.html"
    end
  end

  ...

end

这将在所有请求之前运行,并仅允许那些完全未指定格式或通过指定html格式的请求。其他所有请求都会返回404错误。如果您想返回406不可接受状态码,可以创建public/406.html文件。


2
那并不会真正返回404,它只是将404.html呈现为200响应。您需要添加:status => 404(或:status => :not_found)来返回正确的状态码。同样地,对于406,您需要使用:status => 406(或:status => :not_acceptable)。 - Peeja
1
如果请求使用“accept”标头请求响应类型,则此方法也会失败,因为该信息未存储在参数中。 - zimkies

5

Ben的解决方案可行。

但是,考虑使用responds_to解决方案。这样更加简洁,因为当您不可避免地需要为JavaScript json或xml调用打开一个操作时,它可以提供灵活性。那么您就不必添加

skip_before_filter :allow_only_html_requests, :only => [:show]

我个人喜欢respond_to块; 它非常描述性。

respond_to do |wants|
  wants.html
 end

任何未在该块中指定的格式都会自动导致返回HTTP 406 Not Acceptable。这很好。

1
同意。responds_to块打开了灵活性的世界,如果你能花时间为每个控制器操作添加一个.html的块,那么这是一个更具前瞻性的解决方案。 - Ben

1

我收到了关于HTML文件图像格式的无意义请求,触发了500错误,提示MissingTemplate。我在我的操作末尾添加了以下内容:

def show
  # [...]
  respond_to :html
end

现在,我们不再收到错误报告,而是向那个调皮的请求者发出406。

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