Application controller refactoring: Moving error methods to proper concern.

This commit is contained in:
Thorsten Eckel 2017-01-20 10:45:19 +01:00
parent b82b183863
commit f511995ba6
2 changed files with 83 additions and 95 deletions

View file

@ -2,6 +2,8 @@
require 'exceptions' require 'exceptions'
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
include ErrorHandling
# http_basic_authenticate_with :name => "test", :password => "ttt" # http_basic_authenticate_with :name => "test", :password => "ttt"
helper_method :current_user, helper_method :current_user,
@ -18,15 +20,6 @@ class ApplicationController < ActionController::Base
before_action :transaction_begin, :set_user, :session_update, :user_device_check, :cors_preflight_check before_action :transaction_begin, :set_user, :session_update, :user_device_check, :cors_preflight_check
after_action :transaction_end, :http_log, :set_access_control_headers after_action :transaction_end, :http_log, :set_access_control_headers
rescue_from StandardError, with: :server_error
rescue_from ExecJS::RuntimeError, with: :server_error
rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from ActiveRecord::StatementInvalid, with: :unprocessable_entity
rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity
rescue_from ArgumentError, with: :unprocessable_entity
rescue_from Exceptions::UnprocessableEntity, with: :unprocessable_entity
rescue_from Exceptions::NotAuthorized, with: :unauthorized
# For all responses in this controller, return the CORS access control headers. # For all responses in this controller, return the CORS access control headers.
def set_access_control_headers def set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*' headers['Access-Control-Allow-Origin'] = '*'
@ -658,30 +651,6 @@ class ApplicationController < ActionController::Base
render json: generic_objects, status: :ok render json: generic_objects, status: :ok
end end
def model_match_error(error)
data = {
error: error
}
if error =~ /Validation failed: (.+?)(,|$)/i
data[:error_human] = $1
end
if error =~ /(already exists|duplicate key|duplicate entry)/i
data[:error_human] = 'Object already exists!'
end
if error =~ /null value in column "(.+?)" violates not-null constraint/i
data[:error_human] = "Attribute '#{$1}' required!"
end
if error =~ /Field '(.+?)' doesn't have a default value/i
data[:error_human] = "Attribute '#{$1}' required!"
end
if Rails.env.production? && !data[:error_human].empty?
data[:error] = data[:error_human]
data.delete('error_human')
end
data
end
def model_references_check(object, params) def model_references_check(object, params)
generic_object = object.find(params[:id]) generic_object = object.find(params[:id])
result = Models.references(object, generic_object.id) result = Models.references(object, generic_object.id)
@ -691,68 +660,6 @@ class ApplicationController < ActionController::Base
raise Exceptions::UnprocessableEntity, e raise Exceptions::UnprocessableEntity, e
end end
def not_found(e)
logger.error e.message
logger.error e.backtrace.inspect
respond_to do |format|
format.json { render json: model_match_error(e.message), status: :not_found }
format.any {
@exception = e
@traceback = !Rails.env.production?
file = File.open(Rails.root.join('public', '404.html'), 'r')
render inline: file.read, status: :not_found
}
end
end
def unprocessable_entity(e)
logger.error e.message
logger.error e.backtrace.inspect
respond_to do |format|
format.json { render json: model_match_error(e.message), status: :unprocessable_entity }
format.any {
@exception = e
@traceback = !Rails.env.production?
file = File.open(Rails.root.join('public', '422.html'), 'r')
render inline: file.read, status: :unprocessable_entity
}
end
end
def server_error(e)
logger.error e.message
logger.error e.backtrace.inspect
respond_to do |format|
format.json { render json: model_match_error(e.message), status: 500 }
format.any {
@exception = e
@traceback = !Rails.env.production?
file = File.open(Rails.root.join('public', '500.html'), 'r')
render inline: file.read, status: 500
}
end
end
def unauthorized(e)
message = e.message
if message == 'Exceptions::NotAuthorized'
message = 'Not authorized'
end
error = model_match_error(message)
if error && error[:error]
response.headers['X-Failure'] = error[:error_human] || error[:error]
end
respond_to do |format|
format.json { render json: error, status: :unauthorized }
format.any {
@exception = e
@traceback = !Rails.env.production?
file = File.open(Rails.root.join('public', '401.html'), 'r')
render inline: file.read, status: :unauthorized
}
end
end
# check maintenance mode # check maintenance mode
def check_maintenance_only(user) def check_maintenance_only(user)
return false if Setting.get('maintenance_mode') != true return false if Setting.get('maintenance_mode') != true

View file

@ -0,0 +1,81 @@
module ErrorHandling
extend ActiveSupport::Concern
included do
rescue_from StandardError, with: :internal_server_error
rescue_from ExecJS::RuntimeError, with: :internal_server_error
rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from ActiveRecord::StatementInvalid, with: :unprocessable_entity
rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity
rescue_from ArgumentError, with: :unprocessable_entity
rescue_from Exceptions::UnprocessableEntity, with: :unprocessable_entity
rescue_from Exceptions::NotAuthorized, with: :unauthorized
end
def not_found(e)
log_error_exception(e)
respond_to_exception(e, :not_found)
end
def unprocessable_entity(e)
log_error_exception(e)
respond_to_exception(e, :unprocessable_entity)
end
def internal_server_error(e)
log_error_exception(e)
respond_to_exception(e, :internal_server_error)
end
def unauthorized(e)
error = humanize_error(e.message)
response.headers['X-Failure'] = error.fetch(:error_human, error[:error])
respond_to_exception(e, :unauthorized)
end
private
def log_error_exception(e)
logger.error e.message
logger.error e.backtrace.inspect
end
def respond_to_exception(e, status)
status_code = Rack::Utils.status_code(status)
respond_to do |format|
format.json { render json: humanize_error(e.message), status: status }
format.any {
@exception = e
@traceback = !Rails.env.production?
file = File.open(Rails.root.join('public', "#{status_code}.html"), 'r')
render inline: file.read, status: status
}
end
end
def humanize_error(error)
data = {
error: error
}
case error
when /Validation failed: (.+?)(,|$)/i
data[:error_human] = $1
when /(already exists|duplicate key|duplicate entry)/i
data[:error_human] = 'Object already exists!'
when /null value in column "(.+?)" violates not-null constraint/i
data[:error_human] = "Attribute '#{$1}' required!"
when /Field '(.+?)' doesn't have a default value/i
data[:error_human] = "Attribute '#{$1}' required!"
when 'Exceptions::NotAuthorized'
data[:error] = 'Not authorized'
data[:error_human] = data[:error]
end
if Rails.env.production? && !data[:error_human].empty?
data[:error] = data.delete(:error_human)
end
data
end
end