class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordNotFound, :with => record_not_found #spazzing out
def record_not_found
flash[:error] = 'Could not find specified role'
redirect_to record_not_found_path
true
end
end
How is that wrong? When I try and run a spec i get:
in `<class:ApplicationController>': undefined local variable or method `record_not_found' for ApplicationController:Class (NameError)
Am I missing something Oo
In the :with => record_not_found argument to rescue_from, record_not_found has not been defined yet, so it's raising the error. You should be providing a symbol instead, as in:
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
in keeping with the example in http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html#method-i-rescue_from
First the parameter accepted in rescue_from :with => has to be a string or a symbol
Second you should protect a called method with protected to prevent a possible miss usage
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
# parameter for :with has to be a string or symbol
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
# to prevent an external access
protected
def record_not_found
flash[:error] = 'Could not find specified role'
redirect_to record_not_found_path
true
end
end
Related
So I have a fairly common rescue_from block in a Rails app:
if Rails.env.production?
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: lambda { |exception| render_error 500, exception }
rescue_from Mongoid::Errors::DocumentNotFound, with: lambda { |exception| render_error 404, exception }
end
end
but I want to be able to see the error message if I'm an admin user, so I change the "unless" line to:
unless Rails.application.config.consider_all_requests_local || (current_user.present? && current_user.site_amdin)
but rails complains: "undefined local variable or method `current_user' for ApplicationController:Class"
So how can I access the instance variables, since the code isn't within a block?
I also tried to wrap it in the before_filter block:
before_filter do
if Rails.env.production? || (current_user.present? && current_user.site_admin)
unless Rails.application.config.consider_all_requests_local
Application.rescue_from Exception, with: lambda { |exception| render_error 500, exception }
Application.rescue_from Mongoid::Errors::DocumentNotFound, with: lambda { |exception| render_error 404, exception }
end
end
end
but the app wouldn't run on the server.
"rescue_from" is class-level method and doesn't have access to the instance variables. However, you can access them from a method that is called in with:
if Rails.env.production?
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: :show_exception
rescue_from Mongoid::Errors::DocumentNotFound, with: lambda { |exception| render_error 404, exception }
end
end
# at the end of file
protected
def show_exception(exception)
if current_user.present? && current_user.site_admin
render text: ([exception.message] + exception.backtrace).join('<br />') # render error for admin
else
render_error 500, exception
end
end
If you haven't found a solution yet, you can try this trick:
unless Rails.application.config.consider_all_requests_local || (Thread.current[:user].present? && Thread.current[:user].site_amdin)
I agree this approach has some minuses but it worths trying when other possibilities are exhausted.
I'd like to rescue from a RecordNotFound exception if, and only if, the request is JSON. Now if I was doing this for a skip_before_action, I would do the below:
skip_before_action :verify_authenticity_token, if: :json_request?
Is there syntax for this in rescue_from? Something like:
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found, if: :json_request?
Helper method:
protected
def json_request?
request.format.json?
end
I am assuming if the request is not JSON then you want to raise? If so you should be able to do this
rescue_from ActiveRecord::RecordNotFound { request.format.json? ? record_not_found : super }
OR
rescue_from ActiveRecord::RecordNotFound, with: lambda{|e| request.format.json? ? record_not_found(e) : raise(e) }
These will have identical impact because if a block is given it assigns it to the options[:with] where as if with: is supplied it uses this as the block and ignores any other block passed to it
rescue_from takes a splat argument called *klasses and a block. It then parses *klasses to determine the options passed in of which it only cares about :with. It then applies the :with block to the key(s) which will represent exception class names to handle.
There is no additional options that will be acknowledged.
Please be advised I have not tested this
You could do:
rescue_from ActiveRecord::RecordNotFound do
record_not_found if json_request?
end
I dont think there is another syntax for that exact case :-(
I'm trying to handle 404 errors when using a cms gem. In my past application this 404 method seemed to work but now its not rescuing the error. Any ideas?
# application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
rescue_from ActionController::RoutingError, :with => :render_404
def after_sign_in_path_for(resource)
admin_cms_path
end
private
def render_404(exception = nil)
if exception
logger.info "Rendering 404: #{exception.message}"
end
flash[:error] = 'The page you entered is unavailble, please send us an email if this is unexpected'
redirect_to root_path
end
end
and the routes
# routes.rb
Amskier::Application.routes.draw do
# ...
comfy_route :blog_admin, path: '/admin'
comfy_route :cms_admin, path: '/admin'
root to: "cms/content#show"
comfy_route :cms, path: '/', sitemap: false
end
Add the route to this action of controller:
match 'path' to: 'errors#catch_404'
and create errors controller like this
class ErrorsController < ApplicationController
def catch_404
raise ActionController::RoutingError.new(params[:path])
end
end
after this it should start working.
How does protect_from_forgery with: :exception work?
I'd like to edit the code to look at it and learn from it. However, I cannot find where it is placed as in a higher level of abstraction.
You can find it here on Github : https://github.com/rails/rails/blob/c60be72c5243c21303b067c9c5cc398111cf48c8/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L88
def protect_from_forgery(options = {})
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
prepend_before_action :verify_authenticity_token, options
end
The with: :exception is passed to protection_method_class(:exception). Which does :
def protection_method_class(name)
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
rescue NameError
raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
end
Then this ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify). name.to_s.classify here will be Exception.
Then you can find:
module ProtectionMethods
class Exception
def initialize(controller)
#controller = controller
end
def handle_unverified_request
raise ActionController::InvalidAuthenticityToken
end
end
end
All of this sets the way the invalid authenticity will be handled.
Then it sets a before_action: :verify_authenticity_token.
def verify_authenticity_token
unless verified_request?
logger.warn "Can't verify CSRF token authenticity" if logger
handle_unverified_request
end
end
Which uses the previously defined strategy:
def handle_unverified_request
forgery_protection_strategy.new(self).handle_unverified_request
end
To raise the exception as defined in Exception.
I tried almost everything on web, all I want is to call a method whenever an exception like "ActiveRecord::RecordNotFound" or "No route matches" appears.
Rescues from ApplicationController does not work, but why?
class ApplicationController < ActionController::Base
protect_from_forgery
private
def self.send_report_error(message)
Notifier.page_failure(message).deliver
end
rescue ActiveRecord::RecordNotFound
# handle not found error
send_report_error ActiveRecord::RecordNotFound.to_s
rescue ActiveRecord::ActiveRecordError
# handle other ActiveRecord errors
send_report_error ActiveRecord::ActiveRecordError.to_s
rescue # StandardError
# handle most other errors
send_report_error "common error"
rescue Exception
# handle everything else
send_report_error "common exception"
end
Use rescue_from. For example:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :send_report_error
end
http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html