Added request methods criteria for the router component.

Now one can decide

map_agent_with_request_methods ("/foo/bar/{bar_id}", agent handle_foo_bar, <<"GET">>)
(and similar for non agent way)
This might be useful in pure RESTful environment.
This commit is contained in:
Jocelyn Fiat
2011-09-07 12:14:03 +02:00
parent 3c9fce293f
commit 244fdf1b02
5 changed files with 154 additions and 54 deletions

View File

@@ -29,6 +29,11 @@ feature {NONE} -- Initialization
create_router create_router
do do
debug
create {REQUEST_URI_ROUTER} router.make (5)
create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5)
end
-- create {REQUEST_URI_ROUTER} router.make (5) -- create {REQUEST_URI_ROUTER} router.make (5)
create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5) create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5)
end end
@@ -47,6 +52,12 @@ feature {NONE} -- Initialization
create ra.make (agent handle_anonymous_hello) create ra.make (agent handle_anonymous_hello)
router.map ("/hello", ra) router.map ("/hello", ra)
router.map ("/hello.{format}", ra) router.map ("/hello.{format}", ra)
router.map_agent_with_request_methods ("/method/any", agent handle_method_any, Void)
router.map_agent_with_request_methods ("/method/guess", agent handle_method_get_or_post, <<"GET", "POST">>)
router.map_agent_with_request_methods ("/method/custom", agent handle_method_get, <<"GET">>)
router.map_agent_with_request_methods ("/method/custom", agent handle_method_post, <<"POST">>)
end end
feature -- Execution feature -- Execution
@@ -170,6 +181,29 @@ feature -- Execution
execute_hello (req, res, ctx.parameter ("name"), ctx) execute_hello (req, res, ctx.parameter ("name"), ctx)
end end
handle_method_any (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, req.request_method, ctx)
end
handle_method_get (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, "GET", ctx)
end
handle_method_post (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, "POST", ctx)
end
handle_method_get_or_post (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, "GET or POST", ctx)
end
note note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -20,8 +20,8 @@ feature -- Access
request: WGI_REQUEST request: WGI_REQUEST
-- Associated request -- Associated request
path: STRING path: READABLE_STRING_GENERAL
-- ??? -- Associated path
request_content_type (content_type_supported: detachable ARRAY [STRING]): detachable READABLE_STRING_8 request_content_type (content_type_supported: detachable ARRAY [STRING]): detachable READABLE_STRING_8
local local

View File

@@ -7,6 +7,9 @@ note
deferred class deferred class
REQUEST_ROUTER REQUEST_ROUTER
inherit
ITERABLE [TUPLE [handler: REQUEST_HANDLER; id: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
feature -- Registration feature -- Registration
map_default (r: like default_handler) map_default (r: like default_handler)
@@ -17,17 +20,28 @@ feature -- Registration
default_handler := r default_handler := r
end end
map (a_id: STRING; h: REQUEST_HANDLER) map (a_id: READABLE_STRING_8; h: REQUEST_HANDLER)
-- Map handler `h' with `a_id' -- Map handler `h' with `a_id'
do
map_with_request_methods (a_id, h, Void)
end
map_with_request_methods (a_id: READABLE_STRING_8; h: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
-- Map handler `h' with `a_id' and `rqst_methods'
deferred deferred
end end
map_agent (a_id: STRING; a_action: like {REQUEST_AGENT_HANDLER}.action) map_agent (a_id: READABLE_STRING_8; a_action: like {REQUEST_AGENT_HANDLER}.action)
do
map_agent_with_request_methods (a_id, a_action, Void)
end
map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: like {REQUEST_AGENT_HANDLER}.action; rqst_methods: detachable ARRAY [READABLE_STRING_8])
local local
h: REQUEST_AGENT_HANDLER h: REQUEST_AGENT_HANDLER
do do
create h.make (a_action) create h.make (a_action)
map (a_id, h) map_with_request_methods (a_id, h, rqst_methods)
end end
feature -- Execution feature -- Execution
@@ -69,6 +83,48 @@ feature {NONE} -- Access: Implementation
req_path_info_unchanged: req.path_info.same_string (old req.path_info) req_path_info_unchanged: req.path_info.same_string (old req.path_info)
end end
is_matching_request_methods (a_request_method: READABLE_STRING_GENERAL; rqst_methods: like formatted_request_methods): BOOLEAN
-- `a_request_method' is matching `rqst_methods' contents
local
i,n: INTEGER
m: READABLE_STRING_GENERAL
do
if rqst_methods /= Void and then not rqst_methods.is_empty then
m := a_request_method
from
i := rqst_methods.lower
n := rqst_methods.upper
until
i > n or Result
loop
Result := m.same_string (rqst_methods[i])
i := i + 1
end
else
Result := True
end
end
formatted_request_methods (rqst_methods: like formatted_request_methods): detachable ARRAY [READABLE_STRING_8]
-- Formatted request methods values
local
i,l,u: INTEGER
do
if rqst_methods /= Void and then not rqst_methods.is_empty then
l := rqst_methods.lower
u := rqst_methods.upper
create Result.make_filled (rqst_methods[l], l, u)
from
i := l + 1
until
i > u
loop
Result[i] := rqst_methods[i].as_string_8.as_upper
i := i + 1
end
end
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
default_handler: detachable REQUEST_HANDLER default_handler: detachable REQUEST_HANDLER

View File

@@ -10,11 +10,6 @@ class
inherit inherit
REQUEST_ROUTER REQUEST_ROUTER
ITERABLE [REQUEST_HANDLER]
redefine
new_cursor
end
create create
make make
@@ -28,9 +23,9 @@ feature -- Initialization
feature -- Registration feature -- Registration
map (p: STRING; h: REQUEST_HANDLER) map_with_request_methods (p: READABLE_STRING_8; h: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
do do
handlers.force (h, p) handlers.force ([h, p, formatted_request_methods (rqst_methods)])
end end
feature {NONE} -- Access: Implementation feature {NONE} -- Access: Implementation
@@ -40,9 +35,9 @@ feature {NONE} -- Access: Implementation
h: detachable REQUEST_HANDLER h: detachable REQUEST_HANDLER
ctx: detachable REQUEST_HANDLER_CONTEXT ctx: detachable REQUEST_HANDLER_CONTEXT
do do
h := handler_by_path (req.path_info) h := handler_by_path (req.path_info, req.request_method)
if h = Void then if h = Void then
if attached smart_handler_by_path (req.path_info) as info then if attached smart_handler_by_path (req.path_info, req.request_method) as info then
h := info.handler h := info.handler
ctx := handler_context (info.path, req) ctx := handler_context (info.path, req)
end end
@@ -59,30 +54,45 @@ feature {NONE} -- Access: Implementation
end end
end end
smart_handler (req: WGI_REQUEST): detachable TUPLE [path: STRING; handler: REQUEST_HANDLER] smart_handler (req: WGI_REQUEST): detachable TUPLE [path: READABLE_STRING_8; handler: REQUEST_HANDLER]
require require
req_valid: req /= Void and then req.path_info /= Void req_valid: req /= Void and then req.path_info /= Void
do do
Result := smart_handler_by_path (req.path_info) Result := smart_handler_by_path (req.path_info, req.request_method)
ensure ensure
req_path_info_unchanged: req.path_info.same_string (old req.path_info) req_path_info_unchanged: req.path_info.same_string (old req.path_info)
end end
handler_by_path (a_path: STRING): detachable REQUEST_HANDLER handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_GENERAL): detachable REQUEST_HANDLER
require require
a_path_valid: a_path /= Void a_path_valid: a_path /= Void
local
l_handlers: like handlers
l_item: like handlers.item
do do
Result := handlers.item (context_path (a_path)) l_handlers := handlers
from
l_handlers.start
until
l_handlers.after or Result /= Void
loop
l_item := l_handlers.item
if is_matching_request_methods (rqst_method, l_item.request_methods) and a_path.same_string (l_item.uri) then
Result := l_item.handler
end
l_handlers.forth
end
-- Result := handlers.item (context_path (a_path))
ensure ensure
a_path_unchanged: a_path.same_string (old a_path) a_path_unchanged: a_path.same_string (old a_path)
end end
smart_handler_by_path (a_path: STRING): detachable TUPLE [path: STRING; handler: REQUEST_HANDLER] smart_handler_by_path (a_path: READABLE_STRING_8; rqst_method: READABLE_STRING_GENERAL): detachable TUPLE [path: READABLE_STRING_8; handler: REQUEST_HANDLER]
require require
a_path_valid: a_path /= Void a_path_valid: a_path /= Void
local local
p: INTEGER p: INTEGER
l_context_path, l_path: STRING l_context_path, l_path: READABLE_STRING_8
h: detachable REQUEST_HANDLER h: detachable REQUEST_HANDLER
do do
l_context_path := context_path (a_path) l_context_path := context_path (a_path)
@@ -92,7 +102,7 @@ feature {NONE} -- Access: Implementation
p <= 1 or Result /= Void p <= 1 or Result /= Void
loop loop
l_path := l_context_path.substring (1, p - 1) l_path := l_context_path.substring (1, p - 1)
h := handler_by_path (l_path) h := handler_by_path (l_path, rqst_method)
if h /= Void then if h /= Void then
Result := [l_path, h] Result := [l_path, h]
else else
@@ -118,34 +128,33 @@ feature -- Context factory
feature -- Access feature -- Access
new_cursor: HASH_TABLE_ITERATION_CURSOR [REQUEST_HANDLER, STRING] new_cursor: ITERATION_CURSOR [TUPLE [handler: REQUEST_HANDLER; id: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
-- Fresh cursor associated with current structure -- Fresh cursor associated with current structure
do do
Result := handlers.new_cursor Result := handlers.new_cursor
end end
item (a_path: STRING): detachable REQUEST_HANDLER
do
Result := handler_by_path (a_path)
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
handlers: HASH_TABLE [REQUEST_HANDLER, STRING] handlers: ARRAYED_LIST [TUPLE [handler: REQUEST_HANDLER; uri: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
-- Handlers -- Handlers indexed by the template expression
-- see `templates'
context_path (a_path: STRING): STRING context_path (a_path: READABLE_STRING_8): READABLE_STRING_8
-- Prepared path from context which match requirement -- Prepared path from context which match requirement
-- i.e: not empty, starting with '/' -- i.e: not empty, starting with '/'
local local
p: INTEGER p: INTEGER
s: STRING_8
do do
Result := a_path Result := a_path
if Result.is_empty then if Result.is_empty then
Result := "/" Result := "/"
else else
if Result[1] /= '/' then if Result[1] /= '/' then
Result := "/" + Result create s.make_from_string (Result)
s.prepend_character ('/')
Result := s
end end
p := Result.index_of ('.', 1) p := Result.index_of ('.', 1)
if p > 0 then if p > 0 then

View File

@@ -10,11 +10,6 @@ class
inherit inherit
REQUEST_ROUTER REQUEST_ROUTER
ITERABLE [REQUEST_HANDLER]
redefine
new_cursor
end
create create
make make
@@ -31,16 +26,21 @@ feature -- Registration
map_with_uri_template (uri: URI_TEMPLATE; h: REQUEST_HANDLER) map_with_uri_template (uri: URI_TEMPLATE; h: REQUEST_HANDLER)
do do
handlers.force (h, uri.template) map_with_uri_template_and_request_methods (uri, h, Void)
end
map_with_uri_template_and_request_methods (uri: URI_TEMPLATE; h: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
do
handlers.force ([h, uri.template, formatted_request_methods (rqst_methods)])
templates.force (uri, uri.template) templates.force (uri, uri.template)
end end
map (tpl: STRING; h: REQUEST_HANDLER) map_with_request_methods (tpl: READABLE_STRING_8; h: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
local local
uri: URI_TEMPLATE uri: URI_TEMPLATE
do do
create uri.make (tpl) create uri.make (tpl)
map_with_uri_template (uri, h) map_with_uri_template_and_request_methods (uri, h, rqst_methods)
end end
feature {NONE} -- Access: Implementation feature {NONE} -- Access: Implementation
@@ -51,20 +51,26 @@ feature {NONE} -- Access: Implementation
l_handlers: like handlers l_handlers: like handlers
t: STRING t: STRING
p: STRING p: STRING
l_req_method: READABLE_STRING_GENERAL
do do
p := req.request_uri p := req.request_uri
from from
l_req_method := req.request_method
l_handlers := handlers l_handlers := handlers
l_handlers.start l_handlers.start
until until
l_handlers.after or Result /= Void l_handlers.after or Result /= Void
loop loop
t := l_handlers.key_for_iteration if attached l_handlers.item as l_info then
if attached templates.item (t) as tpl and then if is_matching_request_methods (l_req_method, l_info.request_methods) then
attached tpl.match (p) as res t := l_info.template
then if attached templates.item (t) as tpl and then
ctx := handler_context (p, req, tpl, res) attached tpl.match (p) as res
Result := [l_handlers.item_for_iteration, ctx] then
ctx := handler_context (p, req, tpl, res)
Result := [l_info.handler, ctx]
end
end
end end
l_handlers.forth l_handlers.forth
end end
@@ -81,26 +87,21 @@ feature -- Context factory
end end
end end
feature -- Access feature -- Access: ITERABLE
new_cursor: HASH_TABLE_ITERATION_CURSOR [REQUEST_HANDLER, STRING] new_cursor: ITERATION_CURSOR [TUPLE [handler: REQUEST_HANDLER; template: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
-- Fresh cursor associated with current structure -- Fresh cursor associated with current structure
do do
Result := handlers.new_cursor Result := handlers.new_cursor
end end
item (a_path: STRING): detachable REQUEST_HANDLER
do
Result := handlers.item (a_path)
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
handlers: HASH_TABLE [REQUEST_HANDLER, STRING] handlers: ARRAYED_LIST [TUPLE [handler: REQUEST_HANDLER; template: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
-- Handlers indexed by the template expression -- Handlers indexed by the template expression
-- see `templates' -- see `templates'
templates: HASH_TABLE [URI_TEMPLATE, STRING] templates: HASH_TABLE [URI_TEMPLATE, READABLE_STRING_8]
-- URI Template indexed by the template expression -- URI Template indexed by the template expression
context_path (a_path: STRING): STRING context_path (a_path: STRING): STRING