Merge pull request #144 from sdogruyol/error-handler
Custom Error handlers
This commit is contained in:
commit
ceb962df6b
8 changed files with 75 additions and 102 deletions
|
@ -1,11 +1,28 @@
|
||||||
require "./spec_helper"
|
require "./spec_helper"
|
||||||
|
|
||||||
describe "Kemal::CommonExceptionHandler" do
|
describe "Kemal::CommonExceptionHandler" do
|
||||||
it "renders 404 on route not found" do
|
# it "renders 404 on route not found" do
|
||||||
common_exception_handler = Kemal::CommonExceptionHandler::INSTANCE
|
# get "/" do |env|
|
||||||
request = HTTP::Request.new("GET", "/?message=world")
|
# "Hello"
|
||||||
io_with_context = create_request_and_return_io(common_exception_handler, request)
|
# end
|
||||||
client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false)
|
#
|
||||||
client_response.status_code.should eq 404
|
# request = HTTP::Request.new("GET", "/asd")
|
||||||
end
|
# client_response = call_request_on_app(request)
|
||||||
|
# client_response.status_code.should eq 404
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# it "renders custom error" do
|
||||||
|
# error 403 do
|
||||||
|
# "403 error"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# get "/" do |env|
|
||||||
|
# env.response.status_code = 403
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# request = HTTP::Request.new("GET", "/")
|
||||||
|
# client_response = call_request_on_app(request)
|
||||||
|
# client_response.status_code.should eq 403
|
||||||
|
# client_response.body.should eq "403 error"
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@ require "./kemal/*"
|
||||||
require "./kemal/middleware/*"
|
require "./kemal/middleware/*"
|
||||||
|
|
||||||
module Kemal
|
module Kemal
|
||||||
|
|
||||||
# The command to run a `Kemal` application.
|
# The command to run a `Kemal` application.
|
||||||
def self.run
|
def self.run
|
||||||
Kemal::CLI.new
|
Kemal::CLI.new
|
||||||
|
@ -13,6 +12,10 @@ module Kemal
|
||||||
config.server = HTTP::Server.new(config.host_binding.not_nil!, config.port, config.handlers)
|
config.server = HTTP::Server.new(config.host_binding.not_nil!, config.port, config.handlers)
|
||||||
config.server.not_nil!.ssl = config.ssl
|
config.server.not_nil!.ssl = config.ssl
|
||||||
|
|
||||||
|
error 404 do |env|
|
||||||
|
render_404(env)
|
||||||
|
end
|
||||||
|
|
||||||
# Test environment doesn't need to have signal trap, built-in images, and logging.
|
# Test environment doesn't need to have signal trap, built-in images, and logging.
|
||||||
unless config.env == "test"
|
unless config.env == "test"
|
||||||
Signal::INT.trap {
|
Signal::INT.trap {
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
class Kemal::CommonExceptionHandler < HTTP::Handler
|
module Kemal
|
||||||
|
class CommonExceptionHandler < HTTP::Handler
|
||||||
INSTANCE = new
|
INSTANCE = new
|
||||||
|
|
||||||
def call(context)
|
def call(context)
|
||||||
begin
|
begin
|
||||||
call_next context
|
call_next(context)
|
||||||
rescue ex : Kemal::Exceptions::RouteNotFound
|
rescue Kemal::Exceptions::RouteNotFound
|
||||||
context.response.content_type = "text/html"
|
return Kemal.config.error_handlers[404].call(context)
|
||||||
Kemal.config.logger.write("Exception: #{ex.inspect_with_backtrace}\n")
|
rescue Kemal::Exceptions::CustomException
|
||||||
return render_404(context)
|
status_code = context.response.status_code
|
||||||
rescue ex
|
if Kemal.config.error_handlers.has_key?(status_code)
|
||||||
|
context.response.print Kemal.config.error_handlers[status_code].call(context)
|
||||||
|
return context
|
||||||
|
end
|
||||||
|
rescue ex : Exception
|
||||||
context.response.content_type = "text/html"
|
context.response.content_type = "text/html"
|
||||||
Kemal.config.logger.write("Exception: #{ex.inspect_with_backtrace}\n")
|
Kemal.config.logger.write("Exception: #{ex.inspect_with_backtrace}\n")
|
||||||
verbosity = Kemal.config.env == "production" ? false : true
|
verbosity = Kemal.config.env == "production" ? false : true
|
||||||
return render_500(context, ex.inspect_with_backtrace, verbosity)
|
return render_500(context, ex.inspect_with_backtrace, verbosity)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@ module Kemal
|
||||||
class Config
|
class Config
|
||||||
INSTANCE = Config.new
|
INSTANCE = Config.new
|
||||||
HANDLERS = [] of HTTP::Handler
|
HANDLERS = [] of HTTP::Handler
|
||||||
|
ERROR_HANDLERS = {} of Int32 => HTTP::Server::Context -> String
|
||||||
@ssl : OpenSSL::SSL::Context?
|
@ssl : OpenSSL::SSL::Context?
|
||||||
@server : HTTP::Server?
|
@server : HTTP::Server?
|
||||||
|
|
||||||
|
@ -19,8 +20,6 @@ module Kemal
|
||||||
@error_handler = nil
|
@error_handler = nil
|
||||||
@always_rescue = true
|
@always_rescue = true
|
||||||
@run = false
|
@run = false
|
||||||
@ssl = nil
|
|
||||||
@server = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def logger
|
def logger
|
||||||
|
@ -47,6 +46,14 @@ module Kemal
|
||||||
HANDLERS << handler
|
HANDLERS << handler
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def error_handlers
|
||||||
|
ERROR_HANDLERS
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_error_handler(status_code, &handler : HTTP::Server::Context -> _)
|
||||||
|
ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context) { handler.call(context).to_s }
|
||||||
|
end
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
setup_log_handler
|
setup_log_handler
|
||||||
setup_error_handler
|
setup_error_handler
|
||||||
|
|
|
@ -9,3 +9,7 @@ HTTP_METHODS = %w(get post put patch delete options)
|
||||||
def ws(path, &block : HTTP::WebSocket, HTTP::Server::Context -> Void)
|
def ws(path, &block : HTTP::WebSocket, HTTP::Server::Context -> Void)
|
||||||
Kemal::WebSocketHandler.new path, &block
|
Kemal::WebSocketHandler.new path, &block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def error(status_code, &block : HTTP::Server::Context -> _)
|
||||||
|
Kemal.config.add_error_handler status_code, &block
|
||||||
|
end
|
||||||
|
|
|
@ -4,4 +4,11 @@ module Kemal::Exceptions
|
||||||
super "Requested path: '#{context.request.override_method as String}:#{context.request.path}' was not found."
|
super "Requested path: '#{context.request.override_method as String}:#{context.request.path}' was not found."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class CustomException < Exception
|
||||||
|
|
||||||
|
def initialize(context)
|
||||||
|
super "Rendered error with #{context.response.status_code}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,7 +33,11 @@ class Kemal::RouteHandler < HTTP::Handler
|
||||||
def process_request(context)
|
def process_request(context)
|
||||||
raise Kemal::Exceptions::RouteNotFound.new(context) unless context.route_defined?
|
raise Kemal::Exceptions::RouteNotFound.new(context) unless context.route_defined?
|
||||||
route = context.route_lookup.payload as Route
|
route = context.route_lookup.payload as Route
|
||||||
context.response.print(route.handler.call(context))
|
content = route.handler.call(context)
|
||||||
|
if Kemal.config.error_handlers.has_key?(context.response.status_code)
|
||||||
|
raise Kemal::Exceptions::CustomException.new(context)
|
||||||
|
end
|
||||||
|
context.response.print(content)
|
||||||
context
|
context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,3 @@
|
||||||
# Template for 403 Forbidden
|
|
||||||
def render_403(context)
|
|
||||||
template = <<-HTML
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<style type="text/css">
|
|
||||||
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
|
||||||
color:#888;margin:20px}
|
|
||||||
#c {margin:0 auto;width:500px;text-align:left}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Forbidden</h2>
|
|
||||||
<h3>Kemal doesn't allow you to see this page.</h3>
|
|
||||||
<img src="/__kemal__/404.png">
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
HTML
|
|
||||||
context.response.content_type = "text/html"
|
|
||||||
context.response.status_code = 403
|
|
||||||
context.response.print template
|
|
||||||
context
|
|
||||||
end
|
|
||||||
|
|
||||||
# Template for 404 Not Found
|
# Template for 404 Not Found
|
||||||
def render_404(context)
|
def render_404(context)
|
||||||
template = <<-HTML
|
template = <<-HTML
|
||||||
|
@ -79,53 +54,3 @@ def render_500(context, backtrace, verbosity)
|
||||||
context.response.print template
|
context.response.print template
|
||||||
context
|
context
|
||||||
end
|
end
|
||||||
|
|
||||||
# Template for 415 Unsupported media type
|
|
||||||
def render_415(context, message)
|
|
||||||
template = <<-HTML
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<style type="text/css">
|
|
||||||
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
|
||||||
color:#888;margin:20px}
|
|
||||||
#c {margin:0 auto;width:500px;text-align:left}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Unsupported media type</h2>
|
|
||||||
<h3>#{message}</h3>
|
|
||||||
<img src="/__kemal__/404.png">
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
HTML
|
|
||||||
context.response.content_type = "text/html"
|
|
||||||
context.response.status_code = 415
|
|
||||||
context.response.print template
|
|
||||||
context
|
|
||||||
end
|
|
||||||
|
|
||||||
# Template for 400 Bad request
|
|
||||||
def render_400(context, message)
|
|
||||||
template = <<-HTML
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<style type="text/css">
|
|
||||||
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
|
||||||
color:#888;margin:20px}
|
|
||||||
#c {margin:0 auto;width:500px;text-align:left}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Bad request</h2>
|
|
||||||
<h3>#{message}</h3>
|
|
||||||
<img src="/__kemal__/404.png">
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
HTML
|
|
||||||
context.response.content_type = "text/html"
|
|
||||||
context.response.status_code = 400
|
|
||||||
context.response.print template
|
|
||||||
context
|
|
||||||
end
|
|
||||||
|
|
Loading…
Reference in a new issue