Methods for limiting the Rails render format to html
I have a Rails 2.1.2 site that only has html templates e.g. jobs.html.erb, so when开发者_运维百科 I request a restful resource:
www.mysite.com/jobs/1
It renders my job in html, however, if I request:
www.mysite.com/jobs/1.xml
I get the error:
Template is missing Missing template jobs/show.xml.erb in view path c:/workspace/mysite/app/views
What's worse is that I can also request something like
www.mysite.com/jobs/1.xyz
And indeed I see the error:
Template is missing Missing template jobs/show.xyz.erb in view path c:/workspace/mysite/app/views
To stricly present just html content, what is the cleanest and simplest way to tell Rails that I don't want to render anything other than .html.erb files.
It is important to note that:
- Some of my controller actions contain conditional calls to the render() method whilst others use the default Rails behaviour i.e. if you don't call render() then the template named youraction.html.erb will be rendered.
- My code does not use the responds_to() method
It would be great if the solution was not at the render/responds_to level as I would have to modify a significant number of actions. Perhaps there is a way to configure Rails so that only html templates are rendered?
In your routes you can simply remove the line:
map.connect ':controller/:action/:id.:format'
And the ".xyz" will no longer be routed, resulting in 404 errors/.
You can use Rails Per-Action Overwrite feature. What's this? --> It’s also possible to override standard resource handling by passing in a block to respond_with specifying which formats to override for that action:
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 And :only Options
You can also pass in :except and :only options to only support formats for specific actions (as you do with before_filter):
class UsersController < ApplicationController::Base
respond_to :html, :only => :index
respond_to :xml, :json, :except => :show
...
end
The :any Format
If you’re still want to use respond_to within your individual actions, use the :any resource format that can be used as a wildcard match against any unspecified formats:
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
If you don't want to use responds_to, you can do this:
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
That will run before all requests and only let those that do not specify format at all, or that specify html format through. All others get 404'd. You can create a public/406.html if you want to return 406 not acceptable.
Ben's solution works.
Consider the responds_to solution, though. It's cleaner since it allows flexibility when you will inevitably need to open up an action for a JavaScript json or xml call. Then you won't have to add
skip_before_filter :allow_only_html_requests, :only => [:show]
I personally like the respond_to block; it's very descriptive.
respond_to do |wants|
wants.html
end
Any format not specified in the block will automatically cause a HTTP 406 Not Acceptable to be returned. That's nice.
I was getting non-sensical requests for image format on HTML files, triggerring a 500 with MissingTemplate
. I edicted the following at the end of my action:
def show
# [...]
respond_to :html
end
And now instead of getting error reports, we emit a 406 to that mischievious requester.
精彩评论