Skip to content

Instantly share code, notes, and snippets.

@amkisko
Created April 6, 2020 20:23
Show Gist options
  • Save amkisko/3f3cde725bfce3947e7e0036e9130549 to your computer and use it in GitHub Desktop.
Save amkisko/3f3cde725bfce3947e7e0036e9130549 to your computer and use it in GitHub Desktop.
Exceptions middleware for handling low-level errors for Rails with graphql-ruby app
# NOTE: Add `config.exceptions_app = ExceptionsMiddleware.new` to application.rb
class ExceptionsMiddleware
def initialize
end
def call(env)
error = env["action_dispatch.exception"]
request = ActionDispatch::Request.new(env)
log_request_error(error, request)
footprint = "#{request.remote_addr}_#{request.forwarded_for}"
case error
when ActionController::RoutingError
Rack::Attack::Fail2Ban.filter("not-found-#{footprint}", maxretry: 10, findtime: 1.minute, bantime: 1.hour) do
true
end
end
render_error(
message: error.message,
code: error.class.to_s.gsub(/:+/, "_").underscore.upcase,
vars: error.try(:vars)
)
end
private
def render_error(message:, code:, vars:)
error = {
message: message,
extensions: {
code: code,
level: "middleware"
}
}
error[:extensions][:vars] = vars if vars.present?
response = {
data: nil,
errors: [error]
}
# rubocop:disable GitlabSecurity/JsonSerialization
render_format(200, "application/json", response.to_json)
# rubocop:enable GitlabSecurity/JsonSerialization
end
def log_request_error(error, request)
ServerError.log!(
error,
request: request
)
end
def render(status, content_type, body)
format = "to_#{content_type.to_sym}" if content_type
render_format(status, content_type, body.public_send(format))
end
def render_format(status, content_type, body)
[
status,
{
"Content-Type" => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
"Content-Length" => body.bytesize.to_s
},
[body]
]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment