mirror of
https://gitea.invidious.io/iv-org/shard-kemal.git
synced 2024-08-15 00:53:36 +00:00
added verb for filters and minor improvements
This commit is contained in:
parent
62e9170baf
commit
c5201f01ad
13 changed files with 259 additions and 92 deletions
|
@ -3,12 +3,21 @@
|
|||
class HTTP::Server
|
||||
class Context
|
||||
def params
|
||||
@params ||= Kemal::ParamParser.new(@route, @request).parse
|
||||
@params ||= Kemal::ParamParser.new(@request).parse
|
||||
end
|
||||
|
||||
def redirect(url, status_code = 302)
|
||||
@response.headers.add "Location", url
|
||||
@response.status_code = status_code
|
||||
end
|
||||
|
||||
def route_lookup
|
||||
@route_lookup ||= Kemal::RouteHandler::INSTANCE.lookup_route(@request.override_method as String, @request.path)
|
||||
end
|
||||
|
||||
def route_defined?
|
||||
route_lookup.found?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,47 +2,72 @@ module Kemal::Middleware
|
|||
# Kemal::Filter handle all code that should be evaluated before and after
|
||||
# every request
|
||||
class Filter < HTTP::Handler
|
||||
INSTANCE = new
|
||||
|
||||
# This middleware is lazily instantiated and added to the handlers as soon as a call to `after_X` or `before_X` is made.
|
||||
def initialize
|
||||
@tree = Radix::Tree.new
|
||||
Kemal.config.add_handler(self)
|
||||
end
|
||||
|
||||
def add(type, path, options, &block : HTTP::Server::Context -> _)
|
||||
node = radix_path type, path
|
||||
@tree.add node, Block.new &block
|
||||
end
|
||||
|
||||
# The call order of the filters is before_all -> before_x -> X -> after_x -> after_all
|
||||
def call(context)
|
||||
process_filter(context, :before)
|
||||
return call_next(context) unless context.route_defined?
|
||||
call_block_for_path_type("ALL", context.request.path, :before, context)
|
||||
call_block_for_path_type(context.request.override_method, context.request.path, :before, context)
|
||||
call_next(context)
|
||||
process_filter(context, :after)
|
||||
call_block_for_path_type(context.request.override_method, context.request.path, :after, context)
|
||||
call_block_for_path_type("ALL", context.request.path, :after, context)
|
||||
context
|
||||
end
|
||||
|
||||
def filter_for_path_type_defined?(path, type)
|
||||
lookup = @tree.find radix_path(type, path)
|
||||
# This checks is filter is already defined for the verb/path/type combination
|
||||
def filter_for_path_type_defined?(verb, path, type)
|
||||
lookup = @tree.find radix_path(verb, path, type)
|
||||
lookup.found? && lookup.payload.is_a? Block
|
||||
end
|
||||
|
||||
private def process_filter(context, type)
|
||||
lookup = @tree.find radix_path(type, context.request.path)
|
||||
# :nodoc: This shouldn't be called directly, it's not private because I need to call it for testing purpose since I can't call the macros in the spec.
|
||||
# It adds the block for the corresponding verb/path/type combination to the tree.
|
||||
def _add_route_filter(verb, path, type, &block : HTTP::Server::Context -> _)
|
||||
node = radix_path(verb, path, type)
|
||||
@tree.add node, Block.new &block
|
||||
end
|
||||
|
||||
# This can be called directly but it's simpler to just use the macros, it will check if another filter is not already defined for this verb/path/type and proceed to call `add_route_filter`
|
||||
def before(verb, path = "*", &block : HTTP::Server::Context -> _)
|
||||
raise Kemal::Middleware::Filter::BeforeFilterAlreadyDefinedException.new(verb, path) if filter_for_path_type_defined?(verb, path, :before)
|
||||
_add_route_filter verb, path, :before, &block
|
||||
end
|
||||
|
||||
# This can be called directly but it's simpler to just use the macros, it will check if another filter is not already defined for this verb/path/type and proceed to call `add_route_filter`
|
||||
def after(verb, path = "*", &block : HTTP::Server::Context -> _)
|
||||
raise Kemal::Middleware::Filter::AfterFilterAlreadyDefinedException.new(verb, path) if filter_for_path_type_defined?(verb, path, :after)
|
||||
_add_route_filter verb, path, :after, &block
|
||||
end
|
||||
|
||||
# This will fetch the block for the verb/path/type from the tree and call it.
|
||||
private def call_block_for_path_type(verb, path, type, context)
|
||||
lookup = @tree.find radix_path(verb, path, type)
|
||||
if lookup.found? && lookup.payload.is_a? Block
|
||||
block = lookup.payload as Block
|
||||
block.block.call(context)
|
||||
end
|
||||
end
|
||||
|
||||
private def radix_path(type : Symbol, path)
|
||||
"/#{type}#{path}"
|
||||
private def radix_path(verb, path, type : Symbol)
|
||||
"#{type}/#{verb}/#{path}"
|
||||
end
|
||||
|
||||
class BeforeFilterAlreadyDefinedException < Exception
|
||||
def initialize(path)
|
||||
super "A before-filter is already defined for path: '#{path}'."
|
||||
def initialize(verb, path)
|
||||
super "A before-filter is already defined for path: '#{verb}:#{path}'."
|
||||
end
|
||||
end
|
||||
|
||||
class AfterFilterAlreadyDefinedException < Exception
|
||||
def initialize(path)
|
||||
super "An after-filter is already defined for path: '#{path}'."
|
||||
def initialize(verb, path)
|
||||
super "An after-filter is already defined for path: '#{verb}:#{path}'."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -55,22 +80,15 @@ module Kemal::Middleware
|
|||
end
|
||||
end
|
||||
|
||||
def add_filters
|
||||
unless filter = Kemal.config.handlers.any? { |handler| handler.is_a? Kemal::Middleware::Filter }
|
||||
filter = Kemal::Middleware::Filter.new
|
||||
Kemal.config.add_handler filter
|
||||
end
|
||||
filter
|
||||
end
|
||||
# All the helper methods available are:
|
||||
# - before_all, before_get, before_post, before_put, before_patch, before_delete
|
||||
# - after_all, after_get, after_post, after_put, after_patch, after_delete
|
||||
|
||||
def before(path = "*", options = {} of Symbol => String, &block : HTTP::Server::Context -> _)
|
||||
filter = (Kemal.config.handlers.find { |handler| handler.is_a? Kemal::Middleware::Filter } || add_filters) as Kemal::Middleware::Filter
|
||||
raise Kemal::Middleware::Filter::BeforeFilterAlreadyDefinedException.new(path) if filter.filter_for_path_type_defined?(path, :before)
|
||||
filter.add :before, path, options, &block
|
||||
end
|
||||
|
||||
def after(path = "*", options = {} of Symbol => String, &block : HTTP::Server::Context -> _)
|
||||
filter = (Kemal.config.handlers.find { |handler| handler.is_a? Kemal::Middleware::Filter } || add_filters) as Kemal::Middleware::Filter
|
||||
raise Kemal::Middleware::Filter::AfterFilterAlreadyDefinedException.new(path) if filter.filter_for_path_type_defined?(path, :after)
|
||||
filter.add :after, path, options, &block
|
||||
end
|
||||
ALL_METHODS = %w(get post put patch delete all)
|
||||
{% for type in ["before", "after"]%}
|
||||
{% for method in ALL_METHODS %}
|
||||
def {{type.id}}_{{method.id}}(path = "*", &block : HTTP::Server::Context -> _)
|
||||
Kemal::Middleware::Filter::INSTANCE.{{type.id}}({{method}}.upcase, path, &block)
|
||||
end
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
|
|
@ -9,7 +9,7 @@ class Kemal::ParamParser
|
|||
URL_ENCODED_FORM = "application/x-www-form-urlencoded"
|
||||
APPLICATION_JSON = "application/json"
|
||||
|
||||
def initialize(@route, @request)
|
||||
def initialize(@request)
|
||||
@params = {} of String => AllParamTypes
|
||||
end
|
||||
|
||||
|
|
|
@ -2,4 +2,28 @@
|
|||
class HTTP::Request
|
||||
property override_method
|
||||
property url_params
|
||||
|
||||
def override_method
|
||||
@override_method ||= check_for_method_override!
|
||||
end
|
||||
|
||||
# Checks if request params contain _method param to override request incoming method
|
||||
private def check_for_method_override!
|
||||
@override_method = @method
|
||||
if @method == "POST"
|
||||
params = Kemal::ParamParser.new(self).parse_request
|
||||
if params.has_key?("_method") && HTTP::Request.override_method_valid?(params["_method"])
|
||||
@override_method = params["_method"]
|
||||
end
|
||||
end
|
||||
@override_method
|
||||
end
|
||||
|
||||
# Checks if method contained in _method param is valid one
|
||||
def self.override_method_valid?(override_method)
|
||||
return false unless override_method.is_a?(String)
|
||||
override_method = override_method.upcase
|
||||
override_method == "PUT" || override_method == "PATCH" || override_method == "DELETE"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -8,21 +8,5 @@ class Kemal::Route
|
|||
def initialize(@method, @path, &@handler : HTTP::Server::Context -> _)
|
||||
end
|
||||
|
||||
# Checks if request params contain _method param to override request incoming method
|
||||
def self.check_for_method_override!(request)
|
||||
request.override_method = request.method
|
||||
if request.method == "POST"
|
||||
params = Kemal::ParamParser.new(self, request).parse_request
|
||||
if params.has_key?("_method") && self.override_method_valid?(params["_method"])
|
||||
request.override_method = params["_method"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if method contained in _method param is valid one
|
||||
def self.override_method_valid?(override_method)
|
||||
return false unless override_method.is_a?(String)
|
||||
override_method = override_method.upcase
|
||||
return (override_method == "PUT" || override_method == "PATCH" || override_method == "DELETE")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,8 @@ require "radix"
|
|||
class Kemal::RouteHandler < HTTP::Handler
|
||||
INSTANCE = new
|
||||
|
||||
property tree
|
||||
|
||||
def initialize
|
||||
@tree = Radix::Tree.new
|
||||
end
|
||||
|
@ -22,13 +24,16 @@ class Kemal::RouteHandler < HTTP::Handler
|
|||
add_to_radix_tree("HEAD", path, Route.new("HEAD", path, &handler)) if method == "GET"
|
||||
end
|
||||
|
||||
# Check if a route is defined and returns the lookup
|
||||
def lookup_route(verb, path)
|
||||
@tree.find radix_path(verb, path)
|
||||
end
|
||||
|
||||
# Processes the route if it's a match. Otherwise renders 404.
|
||||
def process_request(context)
|
||||
Kemal::Route.check_for_method_override!(context.request)
|
||||
lookup = @tree.find radix_path(context.request.override_method as String, context.request.path)
|
||||
raise Kemal::Exceptions::RouteNotFound.new(context) unless lookup.found?
|
||||
route = lookup.payload as Route
|
||||
context.request.url_params = lookup.params
|
||||
raise Kemal::Exceptions::RouteNotFound.new(context) unless context.route_defined?
|
||||
route = context.route_lookup.payload as Route
|
||||
context.request.url_params = context.route_lookup.params
|
||||
context.response.print(route.handler.call(context).to_s)
|
||||
context
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue