Rendering custom error page from admin namespace - ruby-on-rails

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

Related

raising NotAuthorized exception in application controller

I am trying to raise an exception from application_controller file for any actions other than read
class ApplicationController < ActionController::API
before_action :authenticate_request
attr_reader :current_user
private
def authenticate_request
#current_user = AuthorizeApiRequest.call(request.headers).result
render json: { error: 'Not Authorized' }, status: 401 unless #current_user
end
def authorize!(action)
raise NotAuthorized if action != :read && !current_user.admin?
true
end
end
But when I am making a POST request, the exception itself is throwing error. The following is the error.
NameError (uninitialized constant ApplicationController::NotAuthorized):
How can I fix this without having to add any new modules?
In rails there is no in-built exception called NotAuthorized, so when you try to raise this, rails doesn't know what to do. You can do this in two ways:
You can raise directly using built-in exceptions or create your own. Ex:
raise ActionController::RoutingError.new('Not authorized')
# or
raise ActionController::BadRequest.new('Not authorized')
# or
raise ActionController::MethodNotAllowed.new('Not authorized')
I recommend creating an external module and including it here, it keeps you code clean.

rescue_from ActionController::RoutingError doesn't work

I'm trying to rescue from ActionController::RoutingError and I can't get it to work. I tried almost everything that I could find online, including rescue_from ActionController::RoutingError in Rails 4. I have an errors controller and error pages. I got to work cancan access denied and RecordNotFound, but I can solve the RoutingError.
For cancan I use this inside application_controller.rb
rescue_from CanCan::AccessDenied do
render template: 'errors/error_403', status: 403
end
and I have this in my routes:
match "/404", to: "errors#error_404", via: :all
If I do the same thing for RoutingError it won't work.
I've also tried match '*path', :to => "errors#error_404" but I get erors.
How can I solve this?
Edit: If I do the same thing for RoutingError as for access denied:
rescue_from ActionController::RoutingError do
render template: 'errors/error_404', status: 404
end
it won't work.
The ActionController::RoutingError is raised when Rails tries to match the request with a route. This happens before Rails even initializes a controller - thus your ApplicationController never has a chance to rescue the exception.
Instead the Rails default exceptions_app kicks in - note that this is an app in the Rack sense - it takes a ENV hash with a request and returns a response - in this case the static /public/404.html file.
What you can do is have your Rails app handle rendering the error pages dynamically instead:
# config/application.rb
config.exceptions_app = self.routes # a Rack Application
# config/routes.rb
match "/404", :to => "errors#not_found", :via => :all
match "/500", :to => "errors#internal_server_error", :via => :all
You would then setup a specific controller to handle the error pages - don't do this in your ApplicationController class as you would be adding a not_found and internal_server_error method to all your controllers!
class ErrorsController < ActionController::Base
protect_from_forgery with: :null_session
def not_found
render(status: 404)
end
def internal_server_error
render(status: 500)
end
end
Code borrowed from Matt Brictson: Dynamic Rails Error Pages - read it for the full rundown.
There is a better way to do it:
routes.rb
Rails.application.routes.draw do
match '*unmatched', to: 'application#route_not_found', via: :all
end
application_controller.rb
class ApplicationController < ActionController::Base
def route_not_found
render file: Rails.public_path.join('404.html'), status: :not_found, layout: false
end
end
To test locally, set the following and restart server.
config/development.rb
config.consider_all_requests_local = false
Tested with Rails 6.

Redirect to 404 when rails app crash

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

Disable auto rendering in rails

What I need is to disable the automatic page (HTML) rendering in rails and override it with a after_action method. What I'm trying to achieve is an equivalent of CakePHP $this->autoRender = false;
application_controller.rb
class ApplicationController < ActionController::Base
after_action :custom_render
layout nil # Tried this but didn't worked
def custom_render
render #[...]
end
end
some_controller.rb
class SomeController < ApplicationController
def index
# No rendering here
end
end
As shown in the code I tried to add a layout nil to prevent all actions from rendering, but that doesn't seem to affect the behaviour of the action.
Haven't checked whether it works with Rails 4, but this patch works for Rails 5.
According to the code of BasicImplicitRender and ImplicitRender, send_action of is BasicImplicitRender responsible for calling default_render
Documentation says:
For API controllers, the implicit response is always 204 No Content.
For all other controllers, we use ... heuristics to decide whether to
render a template, raise an error for a missing template, or respond with
204 No Content ...
So I suppose redefining default_render method will serve you purpose.
In your controller:
def a
# uses `default_render` unless you call `render` method explicitly
end
def b
render plain: 'Custom text for b' # `default_render` won't be called
end
private
# This does the trick
#
def default_render
render plain: 'Text'
end
You may also hack send_action just like it is done in Rails so as to even skip default_render call at all:
module ActionController
module BasicImplicitRender # :nodoc:
def send_action(method, *args)
# super.tap { default_render unless performed? }
super
end
end
end
To disable rendering (well return nothing) issue.
def index
render :nothing
end
But it's too late to do anything, as it will return response with empty body.
To disable layout:
def index
render layout: false
end
This will render you view without a layout, issue (render layout: 'my_custom_layout') to render default view but with different layout.
We don't know what you want, but the simplest solution is just to render a specific view, f.i.:
def index
render 'my_custom_file.'
end
There are really many options: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render
EDIT - as requested in a comment
class ApplicationController < ActionController::Base
before_action :set_user_template
# ...
def set_user_template
template_name = current_user.template_name
self.class.layout "#{template_name}/application"
end
end

Custom 404 not firing from controller

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.

Resources