How to handle routing error in Ruby on Rails - ruby-on-rails

I am facing the routing error if the route is not match. I want to redirect the page to 404 static page if the route not match.
I used some references but it's not working.
The following error is displayed when No route matches.
No route matches [GET] "/userss"
Rails.root: D:/Ruby/Assignment/Library

At the end of your routes.rb file write
get '*path' => redirect('your path of static page')

Add these lines in your application_controller.rb
rescue_from ActionController::RoutingError, with: :render_404
private
def render_404
respond_to do |format|
format.html { render "#{Rails.root}/public/404.html", status: 404 }
format.json { render json: { status: 404, message: 'Page Not Found' } }
end
end

Related

How can i deny PROPFIND requests for a specific route? [duplicate]

I've got the following error:
ActionController::RoutingError (No route matches [GET] "/images/favicon.ico")
I want to show error404 page for links that are not existing.
How can I achieve that?
In application_controller.rb add the following:
# You want to get exceptions in development, but not in production.
unless Rails.application.config.consider_all_requests_local
rescue_from ActionController::RoutingError, with: -> { render_404 }
end
def render_404
respond_to do |format|
format.html { render template: 'errors/not_found', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
I usually also rescue following exceptions, but that's up to you:
rescue_from ActionController::UnknownController, with: -> { render_404 }
rescue_from ActiveRecord::RecordNotFound, with: -> { render_404 }
Create the errors controller:
class ErrorsController < ApplicationController
def error_404
render 'errors/not_found'
end
end
Then in routes.rb
unless Rails.application.config.consider_all_requests_local
# having created corresponding controller and action
get '*path', to: 'errors#error_404', via: :all
end
And the last thing is to create not_found.html.haml (or whatever template engine you use) under /views/errors/:
%span 404
%br
Page Not Found
#Andrey Deineko, your solution seems to work only for the RoutingErrors raised manually inside a conrtoller. If I try it with the url my_app/not_existing_path, I still get the standard error message.
I guess this is because the application doesn't even reach the controllers, since Rails raises the error before.
The trick that solved the problem for me was to add the following line at the end of the routes:
Rails.application.routes.draw do
# existing paths
match '*path' => 'errors#error_404', via: :all
end
to catch all not predefined requests.
Then in the ErrorsController you can use respond_to to serve html, json... requests:
class ErrorsController < ApplicationController
def error_404
#requested_path = request.path
repond_to do |format|
format.html
format.json { render json: {routing_error: #requested_path} }
end
end
end
Copying favicon image in app/assets/images worked for me.

Rails 500 error showing blank page for PUT request

I have setup custom error pages in my rails app.
application.rb
config.exceptions_app = self.routes
routes.rb
get '404', to: 'application#page_not_found'
get '422', to: 'application#server_error'
get '500', to: 'application#server_error'
application_controller.rb
def page_not_found
respond_to do |format|
format.html { render template: 'errors/not_found_error', layout: 'layouts/application', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
def server_error
respond_to do |format|
format.html { render template: 'errors/internal_server_error', layout: 'layouts/error', status: 500 }
format.all { render nothing: true, status: 500}
end
end
My custom 500 error page shows up fine when I do a GET request that throws the error but when I submit a form that triggers a NoMethodError, so a PUT request, I just get a blank page.
Any ideas why the 500 error page displays correctly for a GET request but not for a PUT request?
I tried changing the server_error method to
def server_error
render template: 'errors/internal_server_error', layout: 'layouts/error', status: 500
end
but this didn't seem to help.
Let me know if I can provide more code, I'm not sure how to troubleshoot this.
use match and via on your routes.rb to route all types of HTTP requests to custom error actions
# error routes
match '/404' => 'application#page_not_found', :via => :all
match '/422' => 'application#unprocessable_entity', :via => :all
match '/500' => 'application#server_error', :via => :all

Internal Server Error 500 gets thrown instead of 404 while trying to access broken picture urls

We have a rails server with custom 404 and 500 pages setup using this tutorial here:
http://ramblinglabs.com/blog/2012/01/rails-3-1-adding-custom-404-and-500-error-pages
While it works nice and throws 404s for all kinds of paths, it generates internal server errors 500 while trying to access any kind of suffixed path like en/foo.png, en/foo.pdf, en/foo.xml, ...
But something like en/file.foo throws 404. So only valid suffixes throw a 500.
End of routes.rb:
if Rails.application.config.consider_all_requests_local
match '*not_found', to: 'errors#error_404'
end
application_controller.rb
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: :render_500
rescue_from ActionController::RoutingError, with: :render_404
rescue_from ActionController::UnknownController, with: :render_404
rescue_from ::AbstractController::ActionNotFound, with: :render_404
rescue_from ActiveRecord::RecordNotFound, with: :render_404
end
protected
def render_404(exception)
#not_found_path = exception.message
respond_to do |format|
format.html { render template: 'errors/error_404', layout: 'layouts/application', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
def render_500(exception)
logger.fatal(exception)
respond_to do |format|
format.html { render template: 'errors/error_500', layout: 'layouts/application', status: 500 }
format.all { render nothing: true, status: 500}
end
end
500 that appears:
Missing template errors/error_404 with {:locale=>[:de, :en], :formats=>[:png], :handlers=>[:erb, :builder, :coffee, :arb, :haml]}
We found the mistake.
We had an error_controller.rb containing this:
def error_404
#not_found_path = params[:not_found]
render template: 'errors/error_404', layout: 'layouts/application', status: 404
end
and we changed it to fix this problem to:
def error_404
#not_found_path = params[:not_found]
respond_to do |format|
format.html { render template: 'errors/error_404', layout: 'layouts/application', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
Try adding
respond_to :html, :json, :png
and any other necessary formats at the top of your controller. If I'm right, then the problem is that format.all in the individual controller actions isn't set up to include :png as one of the formats it responds to.
You will probably also need to add to your config/environment.rb the following definition and any similar ones:
Mime::Type.register "image/png", :png
See more details here. Basically you need to set up the mime types that you want to respond to. The error message indicates that rails doesn't understand how to render the format png.

Ruby on Rails: How do I change the behavior of RecordNotFound?

When going to on object's show page with an id that doesn't exist, the RecordNotFonud exception is thown. Is there a way I can redirect to some error page, or maybe a different action when this error is thrown?
You may use rescue_from if you are using Rails 3:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :render_404
def render_404
respond_to do |format|
format.html { render :action => "errors/404.html.erb", :status => 404 }
# and so on..
end
end
end
Yes, you can also do a redirect instead of render, but this is not a good idea. Any semi-automatic interaction with your site will think that the transfer was successfull (because the returned code was not 404), but the received resource was not the one your client wanted.
In development mode you'll see the exception details but it should automatically render the 404.html file from your public directory when your app is running in production mode.
See http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html. Rails has nice features for exception handling.
I generally do something like this in my ApplicationController
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :routing_error
private
def routing_error
redirect_to(root_url, :alert => "Sorry, the page you requested could not be found.")
end
end
If you need to handle more than one specific exception, use rescue_action or rescue_action_in_public, the difference in to hook local requests or not (development/production in common). I prefer to use in_public , because need to review exception's backtrace in development mode.
take a look at my source code:
class ApplicationController < ActionController::Base
include CustomExceptionsHandler
....
end
module CustomExceptionsHandler
# Redirect to login/dashboard path when Exception is caught
def rescue_action_in_public(exception)
logger.error("\n !!! Exception !!! \n #{exception.message} \n")
case exception.class.to_s
when "Task::AccessDenied"
logger.error(" !!! 403 !!!")
notify_hoptoad(exception) //catch this kind of notification to Hoptoad
render_403
when "AuthenticatedSystem::PermissionDenied"
logger.error(" !!! 403 !!!")
render_403
when "Task::MissingDenied"
logger.error(" !!! 404 !!!")
notify_hoptoad(exception)
render_404
when "ActionController::RoutingError"
logger.error(" !!! 404 !!!")
render_404
else
notify_hoptoad(exception)
redirect_to(current_user.nil? ? login_path : dashboard_path) and return false
end
end
private
#403 Forbidden
def render_403
respond_to do |format|
format.html { render :template => "common/403", :layout => false, :status => 403 }
format.xml { head 403 }
format.js { head 403 }
format.json { head 403 }
end
return false
end
#404 Not Found
def render_404
respond_to do |format|
format.html { render :template => "common/404", :layout => false, :status => 404 }
format.xml { head 404 }
format.js { head 404 }
format.json { head 404 }
end
return false
end
end
Use a begin - rescue - end construct to catch the exception and do something useful with it.
userid=2
begin
u=User.find userid
rescue RecordNotFound
redirect_to "/errorpage" #Go to erropage if you didn't find the record
exit
end
redirect_to u # Go to the user page

How to redirect to a 404 in Rails?

I'd like to 'fake' a 404 page in Rails. In PHP, I would just send a header with the error code as such:
header("HTTP/1.0 404 Not Found");
How is that done with Rails?
Don't render 404 yourself, there's no reason to; Rails has this functionality built in already. If you want to show a 404 page, create a render_404 method (or not_found as I called it) in ApplicationController like this:
def not_found
raise ActionController::RoutingError.new('Not Found')
end
Rails also handles AbstractController::ActionNotFound, and ActiveRecord::RecordNotFound the same way.
This does two things better:
1) It uses Rails' built in rescue_from handler to render the 404 page, and
2) it interrupts the execution of your code, letting you do nice things like:
user = User.find_by_email(params[:email]) or not_found
user.do_something!
without having to write ugly conditional statements.
As a bonus, it's also super easy to handle in tests. For example, in an rspec integration test:
# RSpec 1
lambda {
visit '/something/you/want/to/404'
}.should raise_error(ActionController::RoutingError)
# RSpec 2+
expect {
get '/something/you/want/to/404'
}.to raise_error(ActionController::RoutingError)
And minitest:
assert_raises(ActionController::RoutingError) do
get '/something/you/want/to/404'
end
OR refer more info from Rails render 404 not found from a controller action
HTTP 404 Status
To return a 404 header, just use the :status option for the render method.
def action
# here the code
render :status => 404
end
If you want to render the standard 404 page you can extract the feature in a method.
def render_404
respond_to do |format|
format.html { render :file => "#{Rails.root}/public/404", :layout => false, :status => :not_found }
format.xml { head :not_found }
format.any { head :not_found }
end
end
and call it in your action
def action
# here the code
render_404
end
If you want the action to render the error page and stop, simply use a return statement.
def action
render_404 and return if params[:something].blank?
# here the code that will never be executed
end
ActiveRecord and HTTP 404
Also remember that Rails rescues some ActiveRecord errors, such as the ActiveRecord::RecordNotFound displaying the 404 error page.
It means you don't need to rescue this action yourself
def show
user = User.find(params[:id])
end
User.find raises an ActiveRecord::RecordNotFound when the user doesn't exist. This is a very powerful feature. Look at the following code
def show
user = User.find_by_email(params[:email]) or raise("not found")
# ...
end
You can simplify it by delegating to Rails the check. Simply use the bang version.
def show
user = User.find_by_email!(params[:email])
# ...
end
The newly Selected answer submitted by Steven Soroka is close, but not complete. The test itself hides the fact that this is not returning a true 404 - it's returning a status of 200 - "success". The original answer was closer, but attempted to render the layout as if no failure had occurred. This fixes everything:
render :text => 'Not Found', :status => '404'
Here's a typical test set of mine for something I expect to return 404, using RSpec and Shoulda matchers:
describe "user view" do
before do
get :show, :id => 'nonsense'
end
it { should_not assign_to :user }
it { should respond_with :not_found }
it { should respond_with_content_type :html }
it { should_not render_template :show }
it { should_not render_with_layout }
it { should_not set_the_flash }
end
This healthy paranoia allowed me to spot the content-type mismatch when everything else looked peachy :) I check for all these elements: assigned variables, response code, response content type, template rendered, layout rendered, flash messages.
I'll skip the content type check on applications that are strictly html...sometimes. After all, "a skeptic checks ALL the drawers" :)
http://dilbert.com/strips/comic/1998-01-20/
FYI: I don't recommend testing for things that are happening in the controller, ie "should_raise". What you care about is the output. My tests above allowed me to try various solutions, and the tests remain the same whether the solution is raising an exception, special rendering, etc.
You could also use the render file:
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
Where you can choose to use the layout or not.
Another option is to use the Exceptions to control it:
raise ActiveRecord::RecordNotFound, "Record not found."
The selected answer doesn't work in Rails 3.1+ as the error handler was moved to a middleware (see github issue).
Here's the solution I found which I'm pretty happy with.
In ApplicationController:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: :handle_exception
end
def not_found
raise ActionController::RoutingError.new('Not Found')
end
def handle_exception(exception=nil)
if exception
logger = Logger.new(STDOUT)
logger.debug "Exception Message: #{exception.message} \n"
logger.debug "Exception Class: #{exception.class} \n"
logger.debug "Exception Backtrace: \n"
logger.debug exception.backtrace.join("\n")
if [ActionController::RoutingError, ActionController::UnknownController, ActionController::UnknownAction].include?(exception.class)
return render_404
else
return render_500
end
end
end
def render_404
respond_to do |format|
format.html { render template: 'errors/not_found', layout: 'layouts/application', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
def render_500
respond_to do |format|
format.html { render template: 'errors/internal_server_error', layout: 'layouts/application', status: 500 }
format.all { render nothing: true, status: 500}
end
end
and in application.rb:
config.after_initialize do |app|
app.routes.append{ match '*a', :to => 'application#not_found' } unless config.consider_all_requests_local
end
And in my resources (show, edit, update, delete):
#resource = Resource.find(params[:id]) or not_found
This could certainly be improved, but at least, I have different views for not_found and internal_error without overriding core Rails functions.
these will help you...
Application Controller
class ApplicationController < ActionController::Base
protect_from_forgery
unless Rails.application.config.consider_all_requests_local
rescue_from ActionController::RoutingError, ActionController::UnknownController, ::AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, with: lambda { |exception| render_error 404, exception }
end
private
def render_error(status, exception)
Rails.logger.error status.to_s + " " + exception.message.to_s
Rails.logger.error exception.backtrace.join("\n")
respond_to do |format|
format.html { render template: "errors/error_#{status}",status: status }
format.all { render nothing: true, status: status }
end
end
end
Errors controller
class ErrorsController < ApplicationController
def error_404
#not_found_path = params[:not_found]
end
end
views/errors/error_404.html.haml
.site
.services-page
.error-template
%h1
Oops!
%h2
404 Not Found
.error-details
Sorry, an error has occured, Requested page not found!
You tried to access '#{#not_found_path}', which is not a valid page.
.error-actions
%a.button_simple_orange.btn.btn-primary.btn-lg{href: root_path}
%span.glyphicon.glyphicon-home
Take Me Home
routes.rb
get '*unmatched_route', to: 'main#not_found'
main_controller.rb
def not_found
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
<%= render file: 'public/404', status: 404, formats: [:html] %>
just add this to the page you want to render to the 404 error page and you are done.
I wanted to throw a 'normal' 404 for any logged in user that isn't an admin, so I ended up writing something like this in Rails 5:
class AdminController < ApplicationController
before_action :blackhole_admin
private
def blackhole_admin
return if current_user.admin?
raise ActionController::RoutingError, 'Not Found'
rescue ActionController::RoutingError
render file: "#{Rails.root}/public/404", layout: false, status: :not_found
end
end
Raising ActionController::RoutingError('not found') has always felt a little bit strange to me - in the case of an unauthenticated user, this error does not reflect reality - the route was found, the user is just not authenticated.
I happened across config.action_dispatch.rescue_responses and I think in some cases this is a more elegant solution to the stated problem:
# application.rb
config.action_dispatch.rescue_responses = {
'UnauthenticatedError' => :not_found
}
# my_controller.rb
before_action :verify_user_authentication
def verify_user_authentication
raise UnauthenticatedError if !user_authenticated?
end
What's nice about this approach is:
It hooks into the existing error handling middleware like a normal ActionController::RoutingError, but you get a more meaningful error message in dev environments
It will correctly set the status to whatever you specify in the rescue_responses hash (in this case 404 - not_found)
You don't have to write a not_found method that needs to be available everywhere.
To test the error handling, you can do something like this:
feature ErrorHandling do
before do
Rails.application.config.consider_all_requests_local = false
Rails.application.config.action_dispatch.show_exceptions = true
end
scenario 'renders not_found template' do
visit '/blah'
expect(page).to have_content "The page you were looking for doesn't exist."
end
end
If you want to handle different 404s in different ways, consider catching them in your controllers. This will allow you to do things like tracking the number of 404s generated by different user groups, have support interact with users to find out what went wrong / what part of the user experience might need tweaking, do A/B testing, etc.
I have here placed the base logic in ApplicationController, but it can also be placed in more specific controllers, to have special logic only for one controller.
The reason I am using an if with ENV['RESCUE_404'], is so I can test the raising of AR::RecordNotFound in isolation. In tests, I can set this ENV var to false, and my rescue_from would not fire. This way I can test the raising separate from the conditional 404 logic.
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :conditional_404_redirect if ENV['RESCUE_404']
private
def conditional_404_redirect
track_404(#current_user)
if #current_user.present?
redirect_to_user_home
else
redirect_to_front
end
end
end

Resources