Custom error pages in Ruby on Rails - ruby-on-rails

I have this code somewhere in my controller:
raise PermissionDenied
When this is executed, I want to show a custom error page written in HAML, rather than the default NameError page.
Can anyone help me? Thanks.

The rescue_from method can be used for global exception handling.
Change theapp/controller/application_controller.rb file to add the exception handler.
class ApplicationController < ActionController::Base
rescue_from ::PermissionDenied, :with => :render_permission_denied
def render_permission_denied(e)
#error = e # Optional, accessible in the error template
log_error(e) # Optional
render :template => 'error_pages/permission_denied', :status => :forbidden
end
end
Now add a haml file called permission_denied.html.haml in app/views/error_pages directory.
%h1 Permission Denied!
%p #{#error.message}
Refer to the rails documentation for more details.

Related

Show detailed error info to some kind of user

On a rails 2.3 application I want to show the detailed error exception information if #user.is_admin? is true. Otherwise show the generic error on 500.rhtml.
What is the best way to approach this?
In your ApplicationController, rescue_from StandardError and in that method, render a view where you print out the exception message and backtrace.
class ApplicationController
rescue_from StandardError, :with => :show_exception
def show_exception(e)
#e = e
render "global/show_exception"
end
end
# app/views/global/show_exception.html.erb
<h1><%= #e.message %></h1>
<pre>
<%= #e.backtrace %>
</pre>
That should do the trick. The other option is to have a before_filter that checks to see if the user is an admin, and then sets the application configuration for consider_all_requests_local to true if so.
before_filter { Rails.application.config.consider_all_requests_local = current_user.try(:is_admin?) }

Get exception content in Rails Router

So I handle exceptions with an error controller to display dynamic content to my users in production. I have it in my route file to do:
# Errors
%w( 404 422 500 ).each do |code|
get code, :to => "errors#show", :code => code
end
The only problem is now that I'm routing on errors such as that I lose information in my controller when I want to notify Airbrake. How can I maintain the exception information and send it to Airbrake on a 500? Right now all I get is the env that was occurring at the time of the exception which is less helpful for debugging purposes.
class ErrorsController < ApplicationController
def show
notify_airbrake(env)
render status_code.to_s, :status => status_code
end
protected
def status_code
params[:code] || 500
end
end
Are you handling an error by redirecting to a URL like http://your.site/500? That will be just an HTTP request like any other, losing the exception context you're after. Instead, you probably want to be using ActionController's Rescue functionality. It would look like this:
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :render_error
private
def render_error(error)
notify_airbrake(error)
render text: 500, status: 500
end
end
You can add multiple rescue_from declarations to handle different kinds of error, like the ActiveRecord::RecordNotFound from the Rails guide's example.

How do I get exceptions to render a plain text version of the Rails development error page?

I am using rails-api to build an API with no web interface. When I get errors in development, I'd love to see just the error message and stacktrace in plain text without all of the HTML wrapping. How do I override the global exception handling so it renders the stacktrace in development mode in plain text/JSON, and a generic error message in production?
I would suggest that including the stack trace in production code is probably not a good idea from a security stand point.
Here is how I would do it:
render :json => {message:exception.message, stack_trace: exception.stacktrace}
I hope this helps.
After Sam's clarification I can add:
In your base controller for your API (probably ApplicationController):
class ApplicationController < ActionController::Base
...
rescue_from Exception do |exception|
error = {message:exception.message}
error[:stack_trace] = exception.stacktrace if Rails.env.development?
render :json => error
end
...
end
Caveat: You may not want to rescue from every single exception in this way but this is how you'd do it if you did.
Some improvements over #donleyp answer to get a clean trace in Rails 3.2 and output generic error info in production:
class ApplicationController < ActionController::API
...
rescue_from Exception do |exception|
if Rails.env.development?
error = {message:exception.message}
error[:application_trace] = Rails.backtrace_cleaner.clean(exception.backtrace)
error[:full_trace] = exception.backtrace
render :json => error
else
render :text => "Internal server error.", :status => 500
end
end
...
end

Should Redirect to a common display/page on Custom Route/Page Not Found error in Rails

I am working on a Rails app. In my app, if I enter a custom route manually in the address bar/ as URL which is not present in config/routes.rb, it will show up the below given error message.
Routing Error
No route matches "/clientImage/blablahblah"
I want this to be redirected to a proper display for all the wrong routes given by the user either intentionally/unintentionally. Any help would be greatly appreciated.
When someone enters unsupported url Rails will raise ActionController::RoutingError. You can rescue this error and render 404 Not Found html.
Rails provides some special function called rescue_from for this purpose.
class ApplicationController < ActionController::Base
rescue_from ActionController::RoutingError, :with => :render_not_found
rescue_from StandardError, :with => :render_server_error
protected
def render_not_found
render "shared/404", :status => 404
end
def render_server_error
render "shared/500", :status => 500
end
end
Put your 404.html, 500.html in app/views/shared
Yourapp::Application.routes.draw do
#Last route in routes.rb
match '*a', :to => 'errors#routing'
end
The "a" is actually a parameter in the Rails 3 Route Globbing technique. For example, if your url was /this-url-does-not-exist, then params[:a] equals "/this-url-does-not-exist". So be as creative as you'd like handling that rogue route.
In app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
def routing
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
end

How to render two different error pages for same error code (e.g. 404) in a single rails application?

I have one rails application in which I have two sections, so I want to use two different layouts for the Error page.
For example, if an error is coming from Section 1 then layout1 / different page should be used for the Error (404, 500).
If error is coming from Section 2 then layout2 / different page should be used for the Error (404, 500).
I've written code to define the Error page, enabled with erb and ruby code.
in application.rb
config.exceptions_app = self.routes
in routes.rb
match "/404", :to => "errors#error_404"
match "/500", :to => "errors#error_500"
Updated
Thought about it a little. If you only have a few types of errors, how about doing it like this?
In your routes.rb, at the very last line, add a
match '/my_segment/*path', :to => 'errors#not_found'
This should match any path that is not defined (which normally throws ActionController::RoutingError) and push it to your global error page.
You can play with play with the segments wildcard above to get your correct path. This should NOT affect your predefined paths, like mydomain.com/controller1.
Below is a more fine grained method of control.
This will help you match any errors from mydomain.com/some_controller/bad_params
def firstController < ApplicationController
def method_in_first_controller
# Do something here
rescue
#error = # Error object here
render :template=>"some_error_template", :status => :not_found # In specific action
end
end
def secondController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_not_found # In secondController
def method_in_second_controller
# Do something
end
protected
def rescue_not_found
#error = # Error object here
render :template => 'some_error_template', :status => :not_found
end
end
def ApplicationController
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_not_found # Globally
protected
def rescue_not_found
#error = # Error object here
render :template => 'application/not_found', :status => :not_found
end
end
Using referrer doesn't seem to get anywhere, sorry for the bad answer yesterday.
In your errors controller you can have a check who is the referrer and have a conditional layout based on that

Resources