How to rescue from RoutingError in rails 3.1 application. If i'm nt mistaken it was possible to use rescue_from RoutingError in application controller but now it's not possible.
There is no great way to handle it, but there are a few workarounds. The discussion here yields the following suggestion:
Routes
Add the following to your routes file:
match "*", :to => "home#routing_error"
and handle the error in this action:
def routing_error
render text: "Not found, sorry", status: :not_found
end
I wasn't able to replicate #matthew-savage's results. However, per the Rails guide on route globbing and this question on another StackOverflow question, I solved this issue like so:
routes.rb
match "*gibberish", :to => "home#routing_error"
notice how I included text after the wildcard. The controller is fine as shown above:
controller/home_controller.rb
....
def routing_error
render text: "Not found, sorry", status: :not_found
end
Good example.
route.rb
Rails 3:
match '*unmatched_route', :to => 'application#raise_not_found!'
Rails 4:
get '*unmatched_route' => 'application#raise_not_found!'
application_controller.rb
def raise_not_found!
raise ActionController::RoutingError.new("No route matches #{params[:unmatched_route]}")
end
Related
We've recently replaced an old HTML site with a shiny new Ruby on Rails one. Unfortunately a lot of the search engines are still looking for pages like 'index.htm' which no longer exist.
We already have a default route which catches bad URLs and gives a 404, but for URLs which end in .htm, I'd prefer to redirect to different page instead with a 302.
This is what we currently use a catch-all:
match '*a', :to => 'errors#routing'
What can I add above that to catch 'index.htm' and anything else *.htm?
Use match '*a.htm' => redirect("/", :status => 302)
Assuming you have Rails routes that are identical to the legacy routes (without .html at the end), you should be able to add an optional :format in your routes. Check out the defalt catch all route definition:
match ':controller(/:action(/:id))(.:format)'
The key is (.:format) at the end. Try adding that to other routes and it should work with legacy and non-legacy URLs.
I tried the first solution without much success. This worked fine though.
match '*a.htm', to: 'errors#redirect_to_home'
where the errors controller is:
# -*- encoding : utf-8 -*-
class ErrorsController < ApplicationController
def routing
render_404
end
def redirect_to_home
redirect_to root_url
end
end
i was wondering if i could add an exception to route globbing in rails. in my routes.rb i have
unless params[:not_found].eql? 'admin_data'
match '*not_found', to: 'errors#error_404'
end
im trying to enforce custom error pages, except when a user visits
myapp.heroku.com/admin_data
it doesn't seem like fetching :not_found as a param works. is there a way to add an exception in routes.rb?
if it helps, in my errors_controller i have..
def error_404
#not_found_path = params[:not_found]
end
thank you
update.
i tried doing just
puts :not_found
puts %{not_found}
but doesn't seem to work either hmmm...im trying to see if i can retrieve the params from the user
It would be much more convenient to define allowed routes in routes.rb and add exception handling in application controller for routing error:
class ApplicationController < ActionController::Base
rescue_from ActionController::RoutingError, :with => :render_not_found
private
def render_not_found
render_error_page_for(404)
end
def render_error_page_for(code)
respond_to do |format|
format.html { render :file => "#{Rails.root}/public/#{code}.html", :status => code, :layout => false }
end
end
i do catch my exception handling in my application controller but unfortunately for admin_data, i don't explicitly set it in routes.rb. it gets configured somewhere in the gem with namespace or something (im not really sure)
but on a positive note... i finally fixed it! i changed my glob and did...
match '*not_found', to: 'errors#error_404', :constraints => {:subdomain => "!(admin_data.)"}
to ignore everything which uses admin_data.
I read a few websites and questions but all were far beyond my level except one site:
http://markconnell.co.uk/posts/2010/02/rails-3-routing-examples
I'm trying to follow Head First Rails which was made for Rails 2 and so far it's been going well enough. I've been able to find the conversions so far, this is really the first place since installation to stump me.
It says in the config/routes.rb file that it should read:
ActionController::Routing::Routes.draw do |map|
map.connect '/ads/:id', :controller=>'ads', :action=>'show'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
First off my routes.rb file defaults that first line to be
Mebay::Application.routes.draw do
So my first attempt read:
Mebay::Application.routes.draw do
map.connect '/ads/:id', :controller=>'ads', :action=>'show'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
When that failed I tried this:
Mebay::Application.routes.draw do'
map '/ads/:id' => 'ads#index'
end
Then I tried the same thing but adding |map| to the first line.
And finally, I tried changing it all to this:
ActionController::Routing::Routes.draw do |map|
map '/ads/:id' => 'ads#index'
end
None of these have worked though. Could someone please help me out here, I'm not sure what I'm doing wrong. I have another book, Ruby On Rails 3 by Hartl but in the Index it doesn't even show anything on Routes so don't even know where to look in there.
Thanks for any help you can offer me!
// EDIT - I also tried with it reading '/ads/':id thinking perhaps that is a mistake since it doesn't make sense to me why :id would be inside quotes.
// EDIT 2 - This is what Rake Routes returns:
WARNING: 'require 'rake/rdoctask'' is deprecated. Please use 'require 'rdoc/task' (in RDoc 2.4.2+)' instead.
at /Users/Dennis/.rvm/gems/ruby-1.9.2-p318#rails3tutorial/gems/rake-0.9.2.2/lib/rake/rdoctask.rb
WARNING: Global access to Rake DSL methods is deprecated. Please include
... Rake::DSL into classes and modules which use the Rake DSL methods
WARNING: DSL method Mebay::Application#task called at /Users/Dennis/.rvm/gems/ruby-1.9.2-p318#rails3tutorial/gems/railties-3.0.1/lib/rails/application.rb:214:in `initialize_tasks'
/ads/:id(.:format) {:controller=>"ads", :action=>"show"}
/:controller/:action/:id(.:format)
/:controller/:action/:id.:format
Error Message:
Routing Error
No route matches "/ads/3"
//EDIT 3 - This is what my 2 controller files look like:
ads_controller:
class AdsController < ApplicationController
end
application_controller:
class ApplicationController < ActionController::Base
protect_from_forgery
end
//EDIT 4 -
Tried in the ads_controller.rb file:
class AdsController < ApplicationController
def show;
end
end
Also tried in the same file:
class AdsController < ApplicationController
end
def show;
end
And each way I tried going to
localhost:3000/mebay/ads/3
localhost:3000/ads/3
localhost:3000/show/ads/3
localhost:3000/mebay/show/ads/3
Same error still "Routing Error. No Route Matches..."
ActionController::Routing::Routes.draw do
match '/ads/:id' => 'ads#show'
match ':controller/:action/:id'
match ':controller/:action/:id.:format'
end
I thoroughly recommend reading the docs: http://apidock.com/rails/ActionDispatch/Routing
I also am using that book and going through for a difference between rails 2 and rails 3.
I got this to work. In routes.rb file all you need to do is add this between the do and the end:
resources :ads
The key is that your routes.rb file is only sending that resource to the right spot. The next place to look is the ads_controller.rb file where you will want to add this:
#Get /ads/1
#Get /ads/1.json
def show
#ad = Ad.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #ad }
end
end
The next part is adding the index which is a bit later in that chapter. You'll want to add this above that:
# GET /ads
# GET /ads.json
def index
#ads = Ad.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #ad }
end
end
Basically if you look at the chapter 1 routes.rb and controller files you'll see that scaffold configures a rails 3 app like that. One more thing that confused me was I was only able to get the javascript to work by putting it in /app/assets/stylesheets and editing application.html.erb and setting the stylesheet link to look like this:
<%= stylesheet_link_tag "application", :media => "default.css" %>
Enjoy
If you are using Rails 3 you don't want to do a Rails 2 tutorial. There are plenty of good rails 3 tutorials out there. For your question about routes, see the blog post below which might help you figure it out.
http://gregmoreno.wordpress.com/2010/08/12/rails-3-upgrade-part-2-routes/
Try making this your Routes file. I had the same problem while doing it from Chapter 2 of Head First Rails. But this resolved it for me
Mebay::Application.routes.draw do
controller 'ads' do
match 'ads/:id' => :show
end
end
We just upgraded our app to Rails 3.2.2 and are now having a routing issue for handling errors.
Per José Valim's blog post, we added the following:
config.exceptions_app = self.routes to config/application.rb
match "/404", :to => "errors#not_found" to config/routes.rb
(and the appropriate controller/views).
The problem is we need ourdomain.com/id to display an index page for a product category of id.
So, now ourdomain.com/404 shows our 404 page, when it should show our category listing page for the category with an id of 404.
How can we work around this?
Is there a way to make the app prepend each error with error_ before it's evaluated by routes?
Or, maybe somehow set config.exceptions_app to reference a namespace in the routes file?
Or, can I create a second route set and set config.exceptions_app = self.second_set_of_routes?
Thanks!
We had the same problem -- error codes colliding with ids for resources at the root level (e.g., collisions between ourdomain.com/:resource_id and ourdomain.com/404).
We modified José Valim's solution by adding a route constraint that only applies when handling an exception:
# Are we handling an exception?
class ExceptionAppConstraint
def self.matches?(request)
request.env["action_dispatch.exception"].present?
end
end
MyApp::Application.routes.draw do
# These routes are only considered when there is an exception
constraints(ExceptionAppConstraint) do
match "/404", :to => "errors#not_found"
match "/500", :to => "errors#internal_server_error"
# Any other status code
match '*a', :to => "errors#unknown"
end
...
# other routes, including 'match "/:resource_id"'
end
(We only stumbled on this solution last night, so it hasn't had much burn-in time. We are using Rails 3.2.8)
There's one solution which I've found so far:
# application_controller.rb
def rescue_404
rescue_action_in_public CustomNotFoundError.new
end
def rescue_action_in_public(exception)
case exception
when CustomNotFoundError, ::ActionController::UnknownAction then
#render_with_layout "shared/error404", 404, "standard"
render template: "shared/error404", layout: "standard", status: "404"
else
#message = exception
render template: "shared/error", layout: "standard", status: "500"
end
end
def local_request?
return false
end
rescue_action_in_public is the method that Rails calls to handle most errors.
local_request? the method tells Rails to stop sucking if it's local request
# config/routes.rb
match '*path', controller: 'application', action: 'rescue_404' \
unless ::ActionController::Base.consider_all_requests_local
It simply says that it can’t find any other route to handle the request (i.e. the *path) it should call the rescue_404 action on the application controller (the first method above).
EDIT
This version worked for me well!
Try to add to application.rb
# 404 catch all route
config.after_initialize do |app|
app.routes.append{ match '*a', to: 'application#render_not_found' } \
unless config.consider_all_requests_local
end
See: https://github.com/rails/rails/issues/671#issuecomment-1780159
It seems that this route is hard coded at the show_exceptions method (see source)
Sorry, but I don't think of a way of doing it besides changing the line 45 on the source above to:
env["PATH_INFO"] = "/error_#{status}"
(what is, needless to say, no solution at all).
It doesn't hurt to ask:If you thought it was nice to have your own error controller implemented so simply and desperately want to have it, than wouldn't it even be more "RESTful" if your route were yourdomain.com/product/:id?
I want to catch unknown action error in Rails 3, that shows "Unknown Action" error on development and the 404.html on production. I tried putting this rescue_from handler on my ApplicationController (and also on an actual controller, just in case) but I still see the ugly error.
I have custom stuff on the 404, and it can't be plain .html file.
My route:
match '/user/:id/:action', controller: 'users'
The URL I'm accessing: /user/elado/xxx
The rescue_from code:
rescue_from AbstractController::ActionNotFound, :with => :action_not_found
def action_not_found
render text: "action_not_found"
end
The error in the browser:
Unknown action
The action 'xxx' could not be found for UsersController
And in the console:
Started GET "/user/elado/xxx" for 127.0.0.1 at 2011-09-07 19:16:27 -0700
AbstractController::ActionNotFound (The action 'xxx' could not be found for UsersController):
Tried also rescue_from ActionController::UnknownAction.
Any suggestions?
Thanks!
rescue_from was slightly broken when Rails 3 came out (still broken in 3.1 too). Basically you can't:
rescue_from ActionController::RoutingError
anymore. See here.
The solution, for now, is what hamiltop recommends. Use a catch all route that goes to your "routing error" route. Make sure you put it at the end of your config\routes.rb file so it is processed last.
# Any routes that aren't defined above here go to the 404
match "*a", :to => "application#routing_error"
def routing_error
render "404", :status => 404
end
Note: This method has one major drawback. If you use an engine such as Jammit or Devise the catch all will route will make Rails ignore the engine's routes.
If you aren't using an engine that has it's own routes then you should be fine.
However, if you do use an engine that defines its own routes see #arikfr's answer.
Using a catch all route to handle 404 erros (as #Seth Jackson suggested) has one major drawback: if you use any Rails engines that define their own routes (such as Jammit) their routes will be ignored.
Better and more compliant solution would be to use a Rack middleware that will catch 404 errors. In one of my projects, I've implemented such Rack middleware that reports these errors to Hoptoad. I've based my implementation on this one: https://github.com/vidibus/vidibus-routing_error, but instead of invoking my Rails app again to handle the 404 error, I do it in the Rack middleware and let nginx to show the 404 page.
If you really want to rescue AbstractController::ActionNotFound in a controller, you can try something like this:
class UsersController < ApplicationController
private
def process(action, *args)
super
rescue AbstractController::ActionNotFound
respond_to do |format|
format.html { render :404, status: :not_found }
format.all { render nothing: true, status: :not_found }
end
end
public
# actions must not be private
end
This overrides the process method of AbstractController::Base that raises AbstractController::ActionNotFound (see source).
Have you tried a catch all route?
http://railscasts.com/episodes/46-catch-all-route
The wildcard route that you are current using is a Bad Idea(tm).
I would recommend defining the routes you care about, and then doing a catchall as the last line of routes.rb (routes defined first in routes.rb trump later definitions). Then you can render whatever page you want (and specify the 404 status code).
Edit: If you really want to use your current approach... (although this seems like it could be deprecated)
def rescue_action(exception)
case exception
when ActionNotFound, UnknownAction then
# Handle these exceptions here
else
super
end
end