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
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.
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)
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
in my controllers I use something like this a lot to verify that a project really belongs to a given user:
private
def authorized_user
#project = Project.find(params[:id])
redirect_to root_path unless current_user?(#project.user)
end
This works great because user A cannot see the projects of user B (he is being forwarded to the root page instead).
However, this works only as long as project URLs are requested that really exist.
For example, the URL http://localhost:3000/projects/1 will either display the user's project OR forward to the root URL (if another user tries to access the project).
But when I try to access a project that doesn't exist at all in the database, e.g. like this:
http://localhost:3000/projects/777
... I get an ugly ActiveRecord::RecordNotFound error:
Couldn't find Person with id=777
What would be the best way to improve the user experience here?
I have never actually deployed a Rails project yet, so I don't even know what this error will look like in production mode.
Can anybody help?
Thanks...
I personally like to use this:
#project = Project.where(id: params[:id]).first
If project does not exist, #project will be nil.
Try:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :render_404
# Render 404 page when record not found
def render_404
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
end
end
First of all, to avoid not authorized users to access projects, you should scope your find methods:
current_user.projects.find(params[:id])
This way you will get "Couldn't find Project with id" error in development. To avoid this, you can use:
current_user.projects.find_by_id(params[:id])
which returns nil instead of exception, but there are good reasons why you normally should not. In a well-written rails app the only time any user would access a project he shouldn't is when he manually changed the id in the url. You want this to be reported in your logs, not silently skipped over.
Finally, to throw 403 Forbidden instead of 404 Not Found, you may consider using one of many authorization gems (cancan by Ryan Bates comes to mind).
Edit: Oh, and in production, ActiveRecord::RecordNotFound will render 404.html page, a.k.a. these are not the projects you are looking for.
Depending on how you want to handle it, you can use
#project = Project.find_by_id(params[:id])
This will let #project be nil if no records found and you will manually have to handle the case.
Another solution is to throw an 404 which makes sense as the resource isn't there. You can easily do this in any controller (or the application controller) by using:
rescue_from ActiveRecord::RecordNotFound, :with => :not_found
def not_found
raise ActionController::RoutingError.new('Not Found')
end
This would result into something like:
class ApplicationController < ..
rescue_from ActiveRecord::RecordNotFound, :with => :not_found
def not_found
raise ActionController::RoutingError.new('Not Found')
end
end
The latter solution will show the user the default 404(NOT FOUND) error page. In the first case you have more control but at the cost of doing it everywhere
Hope that helps.
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.