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
Related
I have custom error pages in a Rails 4 application and a method in ApplicationController that I use in certain places to manually raise a RoutingError:
def not_found
raise ActionController::RoutingError.new('Not Found')
end
The ErrorsController has a file_not_found action with a corresponding view. routes.rb has the following content:
match '/404', to: 'errors#file_not_found', via: :all
All of this allows me to write code such as not_found unless item.available in my controllers to force a 404.
I'm currently getting the following error only when I call not_found from the admin namespace: Missing template [project path]/public/404.html. How can I use not_found from admin using the same views?
I ommitted a detail that I didn't think was relevant but turned out to be crucial: I am using rails_admin. While perusing the source for this excellent gem, I noticed that it defines its own not_found method here:
def not_found
render :file => Rails.root.join('public', '404.html'), :layout => false, :status => 404
end
To override it, I added the following code in config/initializers/rails_admin_not_found.rb:
RailsAdmin::ApplicationController.module_eval do
def not_found
raise ActionController::RoutingError.new('Not Found')
end
end
There is the following code:
def index
#posts = User.find_by(login: params[:user_id]).posts
end
As you can see this code can generate exception if there is no user with some login (nil pointer exception). How can I catch this exception and handle it properly? I know how to catch exceptions in Ruby, but I want to know how to do in a good Rails style. The same problem may occur in different controllers - may be I should create an action wrapper, catch exception and render 500 error?
The easiest way is to use ApplicationController's rescue_from:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render 'my/custom/template', status: 404
end
end
def index
#posts = User.find_by!(login: params[:user_id]).posts
rescue ActiveRecord::RecordNotFound => error
# handle user not found case
end
You can also use rescue_from http://edgeapi.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html if you want to catch the error globally for the controller.
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.
I have the following code:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: :render_exception
rescue_from ActiveRecord::RecordNotFound, with: :render_exception
rescue_from ActionController::UnknownController, with: :render_exception
rescue_from ::AbstractController::ActionNotFound, with: :render_exception
rescue_from ActiveRecord::ActiveRecordError, with: :render_exception
rescue_from NoMethodError, with: :render_exception
end
They all work flawless, except ::AbstractController::ActionNotFound
I've also tried
AbstractController::ActionNotFound
ActionController::UnknownAction
error:
AbstractController::ActionNotFound (The action 'show' could not be found for ProductsController):
This similar question suggests that you can no longer catch an ActionNotFound exception. Check the link for workarounds. This suggestion to use a Rack middleware to catch 404s looks the cleanest to me.
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).
I think we should catch AbstractController::ActionNotFound in ApplicationController. I have tried following that does not appears to be working.
rescue_from ActionController::ActionNotFound, with: :action_not_found
I have found much cleaner way to handle this exception in ApplicationController. To handle the ActionNotFound Exception in your application, you have to override the action_missing method in your application controller.
def action_missing(m, *args, &block)
Rails.logger.error(m)
redirect_to not_found_path # update your application 404 path here
end
Solution Adapted from: coderwall handling exceptions in your rails application
Overriding process, as described by Grégoire in his answer, seems to work. However, the Rails code says to instead override process_action. That doesn't work, though, because process_action never gets called due to the check for action_name in process.
https://github.com/rails/rails/blob/v3.2.21/actionpack/lib/abstract_controller/base.rb#L115
https://github.com/rails/rails/blob/v3.2.21/actionpack/lib/abstract_controller/base.rb#L161
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.