kenn
kenn

Reputation: 3343

Suppressing ActionView::MissingTemplate exception for Rails 3.x

Starting with Rails 3.0, from time to time, I've been receiving an exception notification like this:

ActionView::MissingTemplate: Missing template [...] with {:locale=>[:en],
  :formats=>[:text], :handlers=>[:erb, :builder, :haml]}. Searched in: * [...]

For instance, an arbitrary hand-written URL like http://example.com/some/path/robots.txt raises the error. Not fun.

I reported the problem in this ticket quite a long ago, and been using the patch mentioned here, but the problem persists.

https://rails.lighthouseapp.com/projects/8994/tickets/6022-content-negotiation-fails-for-some-headers-regression

A fix is suggested in this blog post,

http://trevorturk.wordpress.com/2011/12/09/handling-actionviewmissingtemplate-exceptions/

To use this:

respond_to do |format|
  format.js
end

But it doesn't feel right to me, as I'm not interested in overloading an action with multiple formats. In my app, there are separate URLs for HTML and JSON API, so simple render should be sufficient.

Should I just swallow the exception by rescue_from ActionView::MissingTemplate and return 406 myself?

Is there a better way to handle this situation?

Or I can ask this way - in the first place, is there any real-world usefulness in raising this kind of exception on production?

Upvotes: 13

Views: 5228

Answers (6)

Michael Johann
Michael Johann

Reputation: 475

Best that worked for me is in application_controller.rb:

rescue_from ActionView::MissingTemplate, with: :not_found

Upvotes: 4

akbarbin
akbarbin

Reputation: 5105

Try adding

render nothing: true

at the end of your method.

Upvotes: 1

Brian Dunn
Brian Dunn

Reputation: 860

After some source diving I found another way. Put this in an initializer.

ActionDispatch::ExceptionWrapper.rescue_responses.merge! 'ActionView::MissingTemplate' => :not_found

Upvotes: 2

gtd
gtd

Reputation: 17246

If you have a resource that will only ever be served in one format and you want to ignore any Accept header and simply force it to always output the default format you can remove the format from the template filename. So for instance, if you have:

app/views/some/path/robots.txt.erb

You can change it to simply

app/views/some/path/robots.erb

Some schools of thought would say this is a bad thing since you are returning data in a different format from what was requested, however in practice there are a lot of misbehaving user agents, not every site carefully filters content type requests, and consistently returning the same thing is predictable behavior, so I think this is a reasonable way to go.

Upvotes: 1

Kevin Bedell
Kevin Bedell

Reputation: 13404

If there are specific paths that periodically get called that generate errors -- and they are the same set of urls that get called regularly (i.e., robots.txt or whatever) -- the best thing to do if you can is to eliminate them from hitting your rails server to begin with.

How to do this depends on your server stack. One way to do it is to block this in directly in RACK prior to having the url passed into rails.

Another way may be to block it in NGINX or Unicorn, depending on which web listener you're using for your app.

I'd recommend looking into this and then coming back and posting an additional question here on 'How to Block URL's using Rack?" (Or unicorn or nginx or wherever you think it makes sense to block access.

Upvotes: 0

pixeltrix
pixeltrix

Reputation: 981

If you've no need for formatted routes you can disable them with :format => false in your route specification, e.g.

get '/products' => 'products#index', :format => false

This will generate a RoutingError which gets converted to a 404 Not Found. Alternatively you can restrict it to a number of predefined formats:

get '/products' => 'products#index', :format => /(?:|html|json)/

If you want a formatted url but want it restricted to a single format then you can do this:

get '/products.json' => 'products#index', :format => false, :defaults => { :format => 'json' }

There are a number of valid reasons to raise this error in production - a missing file from a deploy for example or perhaps you'd want notification of someone trying to hack your application's urls.

Upvotes: 6

Related Questions