Add status_code to custom exception in Rails 4 - ruby-on-rails

config/application.rb:
config.exceptions_app = self.routes
config.action_dispatch.rescue_responses.merge!(
'Errors::UnauthorizedException' => :unauthorized
)
app/controllers/errors_controller.rb
class ErrorsController < ActionController::Base
layout 'application'
def show
#exception = env["action_dispatch.exception"]
#status_code = params[:code] || ActionDispatch::ExceptionWrapper.new(env, #exception).status_code
render #status_code.to_s, :status => #status_code
end
end
config/routes.rb
%w( 404 422 500 401 ).each do |code|
get code, :to => 'errors#show', :code => code
end
What am I missing here? If I go to /:code, I get the correct response. If I go to a page where Rails will throw a 404 or a 500, I get the correct response. But if I try to raise Errors::UnauthorizedException I will get a 500 instead of a 401. How do I attach the status code I want to this exception?

I encountered the exact same situation and the fix was very simple in my case. I can't explain it any better, but depending on how and where you defined that Error class, it might be a "top-level Constant" (at least thats what my logs said).
So instead of:
config.action_dispatch.rescue_responses.merge!(
'Errors::UnauthorizedException' => :unauthorized
)
do
config.action_dispatch.rescue_responses.merge!(
'UnauthorizedException' => :unauthorized
)

Related

Rails dynamic error pages (404, 422, 500) showing as blank

I'm implementing dynamic error pages into an app, and have a feeling it's looking in the public folder for (now non-existent) templates, rather than following the routes I've set up.
In config/application.rb I've added the line config.exceptions_app = self.routes to account for this.
I've then added the following to my routes:
get "/not-found", :to => "errors#not_found"
get "/unacceptable", :to => "errors#unacceptable"
get "/internal-error", :to => "errors#internal_error"
And the errors controller looks like so:
class ErrorsController < ApplicationController
layout 'errors'
def not_found
render :status => 404
end
def unacceptable
render :status => 422
end
def internal_error
render :status => 500
end
end
Going to /not-found shows the template as it should be, though visiting any non-existing URL (i.e. /i-dont-exist) renders an empty page.
The only reason I could see for this would be that the exception handling needs the routes to be, for example, get "/404", :to => "errors#not_found", though, ironically, it's not finding the route for /404 (and no, that's not just it working :) ).
Any advice, greatly appreciated. Thanks, Steve.
It seems some setting is wrong.
Try this in your routes:
match '/404', to: 'errors#not_found', via: :all (match instead of get)
You mention that in application.rb you have config.exceptions_app = self.routes, that is good. But make sure you are restarting the server before testing your changes.
And make sure your error views files have the same name than the actions in your ErrorsController.
If you are getting any kind of (haha) error in the console, could you post it?
Do this instead:
routes.rb
%w(404 422 500).each do |code|
get code, :to => "errors#show", :code => code
end
errors_controller.rb
class ErrorsController < ApplicationController
def show
render status_code.to_s, :status => status_code
end
protected
def status_code
params[:code] || 500
end
end
inside your config/application.rb ensure you have:
module YourWebsite
class Application < Rails::Application
config.exceptions_app = self.routes
# .. more code ..
end
end
Then you will need the views, obviously ;) don't forget to remove the error pages in your public directory as well.

Redirect to Custom Error page in Rails

In my routes.rb, I have the following:
%w(401 404 422 500 ).each do |code|
get code, :to => "errors#show", :code => code
end
This creates the following routes according to rake routes:
GET /401(.:format) errors#show {:code=>"401"}
GET /404(.:format) errors#show {:code=>"404"}
GET /422(.:format) errors#show {:code=>"422"}
GET /500(.:format) errors#show {:code=>"500"}
Here is my Errors Controller:
class ErrorsController < ApplicationController
def show
Rails.logger.debug("ERROR WITH STATUS #{status_code.to_s}")
render status_code.to_s, :status => status_code
end
def unauthorized
render "401"
end
protected
def status_code
Rails.logger.debug("STATUS CODE: #{params[:code]}")
params[:code] || 500
end
end
And in my views, I have the following pages under the folder "errors"
401.html.erb
404.html.erb
422.html.erb
500.html.erb
I'd like to redirect to an error page in a controller. If I wanted to redirect to 401, how would I do so?
I was able to solve the problem by adding this route to my routes.rb:
get "errors/unauthorized"
This calls the action "unauthorized" in my errors controller:
def unauthorized
render "401"
end

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 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

Show a 404 instead of 500 in Rails

In my rails app I have defined the routes so users can access records like http://mydomain.com/qwe2
But if they type a wrong "qwe2" they get a 500 page. I think a 404 would be more appropriate.
How can I change the error page that is shown? Thanks
Create a catch-all route at the bottom of config/routes.rb:
map.connect '*path', :controller => 'unknown_route'
Then in app/controllers/unknown_route_controller you can do:
class UnknownRouteController < ApplicationController
def index
render :file => "#{Rails.root}/public/404.html", :layout => false,
:status => 404
end
end
This will render your 404 page for any unknown routes.
The only reason you get a 500 code is if your application throws an exception. This could be due to a missing route, where you do not have anything defined that matches that, or because your controller or view has crashed out.
In a production environment you might want to catch all errors generated by your application and present a better error screen, if appropriate, or a 'Not Found' page if required.
For example, a brute-force catch-all exception catcher might be defined as:
class ApplicationController < ActionController::Base
if (Rails.env.production?)
rescue_from Object, :with => :uncaught_exception_rescue
end
protected
def uncaught_exception_rescue(exception)
render(:partial => 'errors/not_found', :status => :not_found)
end
end
Returning a 404-type error is easy if you can tell when you want to do it:
render(:partial => 'errors/not_found', :status => :not_found)
Make sure you have some kind of default route or you will get these errors all the time. Usually this is done by adding a catch-all route at the very end of your routes.rb:
map.default '/*path', :controller => 'default', :action => 'show'
Then you can do whatever you want with this request.

Resources