Limiting rails to only responding to JSON and XML requests - ruby-on-rails

Is there a way to globally define a rails app to only serve json and xml and appropriately error on any other requests?
I'm thinking it's something along the lines of a before_filter and responds_to block in the ApplicationController but that's as far as my investigation has got me.

Just declare it at the class level on your controller, using respond_to. It will apply to all your controllers if you do it on ApplicationController
class ApplicationController < ActionController::Base
respond_to :xml, :json
…
end
Also read about ActionController::Responder class for more options.

To make json response on errors, just add the following code to your application_controller:
rescue_from Exception, :with => :render_error
rescue_from ActiveRecord::RecordNotFound, :with => :render_not_found
rescue_from ActionController::RoutingError, :with => :render_not_found
rescue_from ActionController::UnknownController, :with => :render_not_found
rescue_from ActionController::UnknownAction, :with => :render_not_found
private
def render_not_found(exception)
# logger.info(exception) # for logging
respond_to do |format|
render json: {:error => "404"}, status: 404
end
end
def render_error(exception)
# logger.info(exception) # for logging
respond_to do |format|
render json: {:error => "500"}, status: 500
end
end
public
def some_public_func
#do sthg
end

Related

how to handle ActiveRecord::RecordNotFound in both ActionController::API and ActionController::Base

In this post, the errors are rescued in the both api and base controller methods. But it might not be best approach to handle errors because of some reasons are:
Fat Controllers
DRY
Maintainability
In ActionController::Base, we handled ActiveRecord::RecordNotFound in only ApplicationController. But for ActionController::API i have to rescue ActiveRecord::RecordNotFound in every controller. So are there any best approach for handle this problem?
Using Rails 5 and 'active_model_serializers' gem for api
ActionController::API
module Api
module V1
class UsersController < ActionController::API
before_action :find_user, only: :show
def find_user
#user = User.find(params[:id])
rescue ActiveRecord::RecordNotFound => e
render json: { error: e.to_s }, status: :not_found
end
end
end
end
ActionController::Base
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render file: "#{Rails.root}/public/404", layout: true, status: :not_found
end
end
You can do something like this in application_controller.rb
if Rails.env.production?
rescue_from ActiveRecord::RecordNotFound, with: :render_404
end
def render_404
render json: {meta: meta_response(404, "Record not found")}
end
This would rescue all RecordNotFound exception with 404 but only in production mode.
ActionController::API includes the ActionController::Rescue module which is what provides the rescue_from class method.
I would create an Api::BaseController base class that the Api::V1::UsersController can use instead of using ActionController::API on each controller class. This would allow you have a rescue_from in a single place instead of needing a rescue block on every action.
module Api
class BaseController < ActionController::API
rescue_from ActiveRecord::RecordNotFound, with: :handle_error
private
def handle_error(e)
render json: { error: e.to_s }, status: :bad_request
end
end
module V1
class UsersController < BaseController
def find_user
#user = User.find(params[:id])
end
end
end
end
I'd also further create an Api::V1::BaseController to allow for easier versioning of the APIs. Then, if you decide to change the format of the errors for v2, just move the rescue_from in the Api::BaseController to the Api::V1::BaseController, and add a new rescue_from to the new Api::V2::BaseController.
module Api
class CommonBaseController < ActionController::API
# code common across API versions
end
module V1
class BaseController < CommonBaseController
rescue_from ActiveRecord::RecordNotFound, with: :handle_error
private
def handle_error(e)
render json: { error: e.to_s }, status: :bad_request
end
end
end
module V2
class BaseController < CommonBaseController
# use a custom base error class to make catching errors easier and more standardized
rescue_from BaseError, with: :handle_error
rescue_from ActiveRecord::RecordNotFound, with: :handle_error
private
def handle_error(e)
status, status_code, code, title, detail =
if e.is_a?(ActiveRecord::RecordNotFound)
[:not_found, '404', '104', 'record not found', 'record not found']
else
[
e.respond_to?(:status) ? e.status : :bad_request,
e.respond_to?(:status_code) ? e.status_code.to_s : '400',
e.respond_to?(:code) ? e.code.to_s : '100',
e.respond_to?(:title) ? e.title : e.to_s,
e.respond_to?(:detail) ? e.detail : e.to_s
]
end
render(
json: {
status: status_code,
code: code,
title: title,
detail: detail
},
status: status
)
end
end
end
end

Always response even exception occurs in doorkeeper

I'm using Doorkeeper with rails-api to implement oAuth2 with password workflow:
resource_owner_from_credentials do
FacebookAuthorization.create(params[:username])
end
Right now when exception occurs it displays 500 template html response from rails. What I want to do is rescue any unexpected exception, then I want to custom the response error message base on exception that occurred in json response.
as the classes defined within the doorkeeper API will extend the Application controller, we can define the following in the Application controller
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 ActionController::UnknownAction, with: :render_404
rescue_from ActiveRecord::RecordNotFound, with: :render_404
end
private
#error handling
def render_404(exception)
#not_found_path = exception.message
respond_to do |format|
format.html { render file: 'public/404.html', status: 404, layout: false }
format.all { render nothing: true, status: 404 }
end
end
def render_500(exception)
#error = exception
respond_to do |format|
format.html { render file: 'public/500.html', status: 500, layout: false }
format.all { render nothing: true, status: 500}
end
end
Then you can define the errors specifically in the ErrorsController
class ErrorsController < ActionController::Base
def not_found
if request.url.match('api')
render :json => {:error => "Not Found"}.to_json, :status => 404
else
render file: 'public/404.html', :status => 404, layout: false
end
end
def exception
if request.url.match('api')
render :json => {:error => "Internal Server Error"}.to_json, :status => 500
else
render file: 'public/500.html', :status => 500, layout: false
end
end
end
Hope this helps.

rails custom error exception implementation is bloated, suggestions for drying it up?

In application_controller.rb there is a method to render 404 errors with a custom layout and partial.
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 ActionController::UnknownAction, with: :render_404
rescue_from ActiveRecord::RecordNotFound, with: :render_404
end
This calls any of the methods:
def render_404(exception)
notify exception
#not_found_path = exception.message
respond_to do |format|
format.html { render template: 'errors/error_404', layout: 'layouts/error', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
def render_500(exception)
notify exception
#error = exception
respond_to do |format|
format.html { render template: 'errors/error_404', layout: 'layouts/error', status: 404 }
format.all { render nothing: true, status: 500 }
end
end
Then there is error_controller.rb:
class ErrorsController < ApplicationController
layout "error"
def sub_layout
"left"
end
def error_404
#not_found_path = params[:not_found]
render "errors/error_404"
end
def error_500
render "errors/error_500"
end
end
QUESTION:
This feels very bloated, Is there a way to have it render the custom error page now situated in /app/views/errors/error_404.html.haml ? with custom layout?
I want to delete my error_controller.rb
The default path for rendering 404 and 500 html responses is in the public folder. I'd replace the default 404.html with your error_404.html
render :file => 'public/404.html', :layout => layouts/error

log backtrace, rescue_from and custom error pages

I am trying to create custom error pages in a project of
rails 3.0.20 and ruby 1.8.7.
Anyway in my application controller:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, :with => :render_error
rescue_from ActionController::RoutingError, ActionController::UnknownController, ::AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, :with => :render_error_not_found
end
Then the render error method:
def render_error(exception)
notify_airbrake(exception)
Rails.logger.fatal exception
Rails.logger.fatal exception.backtrace.join("\n")
respond_to do |format|
format.html { render :template => "errors/error_500", :layout => 'layouts/application'}
format.all { render :nothing => true, :status => 500 }
end
end
It seems that now my logs are being filled with a much longer then usual backtrace.
Why is that happening? Is there a way to show just the "important" part of a backtrace?
And is it correct to call airbrake here?
Thanks
Check out http://api.rubyonrails.org/classes/ActiveSupport/BacktraceCleaner.html. Rails uses this to clean up your backrace before display.
You could use it like this:
Rails.backtrace_cleaner.clean(exception.backtrace)
I'm actually looking in the rails source code, because I thought your exception might have been cleaned already by the time it gets to your render_error method.

How to render 500 page in rescue_from

I would like to send e-mail when having an exception in my application and render the regular 500 page. I could not find how to perform the 500 page render:
class ApplicationController < ActionController::Base
rescue_from StandardError do
send_email_of_error
# what goes here?
end
...
end
Raising the exception again will likely to what you want:
rescue_from StandardError do |exception|
send_email_of_error
raise exception
end
You could also call render to render your own page, the docs have an example doing this.
But why reinvent the wheel? The exception notifier gem already does this and is customizable and tested.
This is an approach that maybe fits your needs:
class ApplicationController < ActionController::Base
rescue_from Exception, :with => :render_500
def render_500(exception)
#exception = exception
render :template => "shared/500.html", :status => 500
end
end

Resources