Apparently rescue_from is supposed to catch Exceptions, but this does not work as expected:
class ApplicationController < ActionController::Base
rescue_from ActionController::RoutingError, with: :not_found
def not_found
text: 'Not found'
end
end
Spec:
specify 'Not found' do
visit '/zzz'
expect(page.status_code).to eq 200
end
Failure/Error: visit '/zzz'
ActionController::RoutingError:
No route matches [GET] "/zzz"
Same behavior in development environment.
However, rescuing other errors such as RuntimeError does work as expected.
Docs: https://apidock.com/rails/v6.0.0/ActiveSupport/Rescuable/ClassMethods/rescue_from
Rails 6.0.2
Why can't RoutingError be used with rescue_from? Is RoutingError raised in middleware or by the router before the controller is called? Is there another way to catch RoutingError?
Router errors are raised before reaching a controller, you could have a wildcard route to match anything that's not matched by other routes at the end of the routes.rb file an point that route to a specific controller action.
Something like:
match '*foo', to: 'application#not_found'
(Didn't try that, you may need to tweak it a little but I think the idea is clear)
Related
Basically, I am trying to re-route every pages that doesn't exist to a 404 page under my public folder.
So far what I did on my applications_controller.rb I place the ff:
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
rescue_from ActiveRecord::RecordNotFound, with: :show_404
But still when I tried to visit a page that doesn't exist like: http://localhost:3000/dashboard/pagedo
It's just showing this instead of the 404 page:
What do I need to do, to do this? Do I need to setup some codes on routes.rb?
You can define a match-all route (via: :all means "match all HTTP verbs"), which needs to be placed at the bottom of your config/routes.rb file:
match '*path', via: :all, to: 'errors#page_not_found'
And then handle it in a controller, in the same manner as your exception handling above:
class ErrorsController < ApplicationController
def page_not_found
show_404
end
end
Tom Lord's answer is good if you want to map all of the non-matching routes in Development to a 404 page; however, in Rails all non-matching routes are mapped to the 404 in public/404.html.You can actually test this out by running
RAILS_ENV=production bin/rails s
from the root of your project and visiting a page that doesn't exist. With this you maintain the useful route matching 404 help page in development.
I'm trying to rescue from ActionController::RoutingError and I can't get it to work. I tried almost everything that I could find online, including rescue_from ActionController::RoutingError in Rails 4. I have an errors controller and error pages. I got to work cancan access denied and RecordNotFound, but I can solve the RoutingError.
For cancan I use this inside application_controller.rb
rescue_from CanCan::AccessDenied do
render template: 'errors/error_403', status: 403
end
and I have this in my routes:
match "/404", to: "errors#error_404", via: :all
If I do the same thing for RoutingError it won't work.
I've also tried match '*path', :to => "errors#error_404" but I get erors.
How can I solve this?
Edit: If I do the same thing for RoutingError as for access denied:
rescue_from ActionController::RoutingError do
render template: 'errors/error_404', status: 404
end
it won't work.
The ActionController::RoutingError is raised when Rails tries to match the request with a route. This happens before Rails even initializes a controller - thus your ApplicationController never has a chance to rescue the exception.
Instead the Rails default exceptions_app kicks in - note that this is an app in the Rack sense - it takes a ENV hash with a request and returns a response - in this case the static /public/404.html file.
What you can do is have your Rails app handle rendering the error pages dynamically instead:
# config/application.rb
config.exceptions_app = self.routes # a Rack Application
# config/routes.rb
match "/404", :to => "errors#not_found", :via => :all
match "/500", :to => "errors#internal_server_error", :via => :all
You would then setup a specific controller to handle the error pages - don't do this in your ApplicationController class as you would be adding a not_found and internal_server_error method to all your controllers!
class ErrorsController < ActionController::Base
protect_from_forgery with: :null_session
def not_found
render(status: 404)
end
def internal_server_error
render(status: 500)
end
end
Code borrowed from Matt Brictson: Dynamic Rails Error Pages - read it for the full rundown.
There is a better way to do it:
routes.rb
Rails.application.routes.draw do
match '*unmatched', to: 'application#route_not_found', via: :all
end
application_controller.rb
class ApplicationController < ActionController::Base
def route_not_found
render file: Rails.public_path.join('404.html'), status: :not_found, layout: false
end
end
To test locally, set the following and restart server.
config/development.rb
config.consider_all_requests_local = false
Tested with Rails 6.
i am new in rails. I want to add redirection to "404 page" whenever my "app crashed" or "page not found" or in case of "exception".
If someone have good tutorials please share with me or provide some simple solution.
I read this Rails_admin redirect to 404 but it did not solved my problem.
The Rails guide has a chapter about exception handling. You can use rescue_from to run a custom method, when an exception is raised. The following example is from that guide and should be added to your application_controller:
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "404 Not Found", status: 404
end
You may want to change the exception type to whatever exceptions you want to catch. I guess that you can also catch from every exception by:
rescue_from StandardError, with: :record_not_found
Note: I would consider this a bad practise and would instead just design my 500 error page to look the same then my 404 page.
I have this method in my application_controller.rb
def not_found
raise ActionController::RoutingError.new('Not Found')
end
and then in any controller when I do a find:
#model = Model.find_by(id: params[:id]) or not_found
I'm using Rails 2 at the moment though so it might be slightly different for your version.
For 404 i.e. page not found, we can redirect page to custom route.
For this you need create new route,
match "*path", to: "handle_errors#page_not_found", via: :all
which will redirect to page_not_found action of handle_errors controller.
Obviously you need to create a new controller handle_errors
For any other exception occurred in app, you can handle it using some customer method in application controller as
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found_error_handler
rescue_from ActiveRecord::RecordNotUnique, :with => :default_error_handler
as so on ...
with defination of methods as,
def default_error_handler(e)
render 'handle_errors/page_404', :status => 404
end
def record_not_found_error_handler(e)
render 'handle_errors/record_not_found'
end
I have 6 controllers currently, and not everyone has defined all 6 actions. I'm hoping to route all undefined actions to root_url so we don't have that ugly error message but can't figure out a way to do that. Can anyone help me out?
As others have pointed out in the comments, you probably do not actually want to do what you are asking, however, since you asked it, the solution you are looking for is a few lines added to your ApplicationController.
unless Rails.application.config.consider_all_requests_local
rescue_from AbstractController::ActionNotFound, with: :handle_error
rescue_from ActiveRecord::RecordNotFound, with: :handle_error
rescue_from ActionController::RoutingError, with: :handle_error
rescue_from ActionController::UnknownController, with: :handle_error
rescue_from ActionController::UnknownAction, with: :handle_error
end
protected
def handle_error
redirect_to root_url
end
This will basically catch all errors around missing routes, controllers, and actions and redirect to the root_url. Since it is in your ApplicationController, which all your other controllers derive from, it works for all of your missing things.
Please note that because I wrapped those rescues in that unless, you will still see the errors in your local dev which you want so that you can deal with any real problems.
Again, you are probably better off putting some custom pages around the different error types like 404, 500, etc and then either redirecting the user or letting them navigate themselves.
As of rails 3.2 you can even direct these errors to a specific rack end point (including the app itself) and deal with them however you want. This is much nicer than the solution above, but only works on rails 3.2 or later. For older versions the above solution will work.
To do this you would add this line to your application.rb
config.exceptions_app = self.routes
Then, in your routes file you can do things like this:
match '/404', to: "error_pages#handle_404"
That expects you to have an ErrorPages controller with a handle_404 action but you can also just route it to where ever, including your root_url, whatever that is. You can do this for all of the error codes.
In the undefined actions of each controller, just write:
redirect_to root_url
In my Ruby on Rails application I want to show 404 error page instead of routing error when the given route does not matches or exists in my application. Can anybody help me to make it possible?
If you cannot easily run production mode locally, set the consider_all_requests_local to false in your config/environments/development.rb file.
This is already the default behavior in production. In development environment routing errors are displayed to let the developer notice them and fix them.
If you want to try it, start the server in production mode and check it.
$ script/rails s -e production
in ApplicationController
rescue_from ActiveRecord::RecordNotFound, :with => :rescue404
rescue_from ActionController::RoutingError, :with => :rescue404
def rescue404
#your custom method for errors, you can render anything you want there
end
This return 404 page
In ApplicationController
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :route_not_found
rescue_from ActionController::RoutingError, with: :route_not_found
rescue_from ActionController::UnknownFormat, with: :route_not_found
def route_not_found
render file: Rails.public_path.join('404.html'), status: :not_found, layout: false
end
You could catch exception that is thrown when a route is not found and then render a custom page. Let me know if you need help with the code. There might be many other ways to do this but this definitely works.