This commit is contained in:
Jocelyn Fiat
2011-10-06 17:37:41 +02:00
2 changed files with 345 additions and 173 deletions

View File

@@ -8,48 +8,44 @@ class
ORDER_HANDLER[C -> REQUEST_HANDLER_CONTEXT] ORDER_HANDLER[C -> REQUEST_HANDLER_CONTEXT]
inherit inherit
REQUEST_HANDLER[C] REQUEST_HANDLER[C]
REQUEST_RESOURCE_HANDLER_HELPER[C]
redefine
do_get,
do_post,
do_put,
do_delete
end
SHARED_DATABASE_API SHARED_DATABASE_API
SHARED_EJSON SHARED_EJSON
REFACTORING_HELPER REFACTORING_HELPER
SHARED_ORDER_VALIDATION SHARED_ORDER_VALIDATION
WGI_RESPONSE_STATUS_CODES
feature -- execute feature -- execute
execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Execute request handler -- Execute request handler
do do
if req.request_method.same_string ("GET") then execute_methods(ctx,req,res)
process_get (ctx,req,res)
elseif req.request_method.same_string ("PUT") then
process_put (ctx,req,res)
elseif req.request_method.same_string ("DELETE") then
process_delete (ctx,req,res)
elseif req.request_method.same_string ("POST") then
process_post (ctx,req,res)
end
end end
feature -- API DOC feature -- API DOC
api_doc : STRING = "URI:/order METHOD: POST%N URI:/order/{orderid} METHOD: GET, PUT, DELETE%N" api_doc : STRING = "URI:/order METHOD: POST%N URI:/order/{orderid} METHOD: GET, PUT, DELETE%N"
feature -- HTTP Methods feature -- HTTP Methods
process_get (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) do_get (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Using GET to retrieve resource information.
-- If the GET request is SUCCESS, we response with
-- 200 OK, and a representation of the order
-- If the GET request is not SUCCESS, we response with
-- 404 Resource not found
local local
l_values: HASH_TABLE [STRING_32, STRING]
l_missings: LINKED_LIST [STRING]
l_full: BOOLEAN
l_post: STRING
joc : JSON_ORDER_CONVERTER joc : JSON_ORDER_CONVERTER
parser : JSON_PARSER
l_order : detachable ORDER l_order : detachable ORDER
jv : detachable JSON_VALUE jv : detachable JSON_VALUE
l_location, id : STRING id : STRING
uri : LIST[READABLE_STRING_32] uri : LIST[READABLE_STRING_32]
h : EWF_HEADER h : EWF_HEADER
http_if_not_match : STRING
do do
fixme ("TODO handle error conditions")
if attached req.orig_path_info as orig_path then if attached req.orig_path_info as orig_path then
uri := orig_path.split ('/') uri := orig_path.split ('/')
id := uri.at (3) id := uri.at (3)
@@ -73,18 +69,13 @@ feature -- HTTP Methods
res.write_string (j.representation) res.write_string (j.representation)
end end
else else
handle_resource_not_found_response ("The following resource"+ orig_path+ " is not found ", res) handle_resource_not_found_response ("The following resource"+ orig_path+ " is not found ", req.content_type, res)
end
end end
end end
do_put (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
end
process_put (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local local
l_values: HASH_TABLE [STRING_32, STRING]
l_missings: LINKED_LIST [STRING]
l_full: BOOLEAN
l_post: STRING l_post: STRING
l_location : STRING l_location : STRING
l_order : detachable ORDER l_order : detachable ORDER
@@ -95,7 +86,6 @@ feature -- HTTP Methods
fixme ("Refactor the code, create new abstractions") fixme ("Refactor the code, create new abstractions")
fixme ("Add Header Date to the response") fixme ("Add Header Date to the response")
fixme ("Put implememntation is wrong!!!!") fixme ("Put implememntation is wrong!!!!")
if req.content_length_value > 0 then
req.input.read_stream (req.content_length_value.as_integer_32) req.input.read_stream (req.content_length_value.as_integer_32)
l_post := req.input.last_string l_post := req.input.last_string
l_order := extract_order_request(l_post) l_order := extract_order_request(l_post)
@@ -120,23 +110,15 @@ feature -- HTTP Methods
res.write_string (jv.representation) res.write_string (jv.representation)
end end
else else
handle_bad_request_response(l_post +"%N is not a valid ORDER, maybe the order does not exist in the system",res) handle_bad_request_response(l_post +"%N is not a valid ORDER, maybe the order does not exist in the system",req.content_type,res)
end
else
handle_bad_request_response("Bad request, content_lenght empty",res)
end end
end end
process_delete (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) do_delete (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local local
l_values: HASH_TABLE [STRING_32, STRING]
uri: LIST [READABLE_STRING_32] uri: LIST [READABLE_STRING_32]
l_full: BOOLEAN
id: STRING id: STRING
l_location : STRING
l_order : detachable ORDER
jv : detachable JSON_VALUE
h : EWF_HEADER h : EWF_HEADER
do do
fixme ("TODO handle an Internal Server Error") fixme ("TODO handle an Internal Server Error")
@@ -155,35 +137,43 @@ feature -- HTTP Methods
res.set_status_code (no_content) res.set_status_code (no_content)
res.write_headers_string (h.string) res.write_headers_string (h.string)
else else
handle_resource_not_found_response (orig_path + " not found in this server", res) handle_resource_not_found_response (orig_path + " not found in this server",req.content_type, res)
end end
end end
end end
process_post (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) do_post (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Here the convention is the following. -- Here the convention is the following.
-- POST is used for creation and the server determines the URI -- POST is used for creation and the server determines the URI
-- of the created resource. -- of the created resource.
-- If the request post is SUCCESS, the server will create the order and will response with
-- HTTP_RESPONSE 201 CREATED
-- if the request post is not SUCCESS, the server will response with
-- HTTP_RESPONSE 400 BAD REQUEST, the client send a bad request
-- HTTP_RESPONSE 500 INTERNAL_SERVER_ERROR, when the server can deliver the request
local local
l_values: HASH_TABLE [STRING_32, STRING]
l_missings: LINKED_LIST [STRING]
l_full: BOOLEAN
l_post: STRING l_post: STRING
l_location : STRING
l_msg : STRING
l_order : detachable ORDER l_order : detachable ORDER
jv : detachable JSON_VALUE
h : EWF_HEADER
do do
fixme ("TODO handle an Internal Server Error")
fixme ("Refactor the code, We need an Extract Method tool :)")
if req.content_length_value > 0 then
req.input.read_stream (req.content_length_value.as_integer_32) req.input.read_stream (req.content_length_value.as_integer_32)
l_post := req.input.last_string l_post := req.input.last_string
l_order := extract_order_request(l_post) l_order := extract_order_request(l_post)
fixme ("TODO move to a service method")
if l_order /= Void then if l_order /= Void then
save_order( l_order) save_order( l_order)
compute_response_post (ctx, req, res, l_order)
else
handle_bad_request_response(l_post +"%N is not a valid ORDER", req.content_type,res)
end
end
compute_response_post (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; l_order : ORDER)
local
h: EWF_HEADER
l_msg : STRING
jv : detachable JSON_VALUE
l_location : STRING
do
create h.make create h.make
h.put_status (created) h.put_status (created)
h.put_content_type ("application/json") h.put_content_type ("application/json")
@@ -202,16 +192,9 @@ feature -- HTTP Methods
res.write_headers_string (h.string) res.write_headers_string (h.string)
res.write_string (l_msg) res.write_string (l_msg)
end end
else
handle_bad_request_response(l_post +"%N is not a valid ORDER",res)
end
else
handle_bad_request_response("Bad request, content_lenght empty",res)
end
end end
feature -- Implementation Repository Layer
feature -- Implementation
save_order ( an_order : ORDER) save_order ( an_order : ORDER)
-- save the order to the repository -- save the order to the repository
@@ -263,74 +246,4 @@ feature -- Implementation
end end
end end
handle_bad_request_response (a_description:STRING; res: WGI_RESPONSE_BUFFER )
local
h : EWF_HEADER
do
create h.make
h.put_status (bad_request)
h.put_content_type ("application/json")
h.put_content_length (a_description.count)
h.add_header ("Date:"+ ((create{HTTP_DATE_TIME_UTILITIES}).now_utc).formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
res.set_status_code (bad_request)
res.write_headers_string (h.string)
res.write_string (a_description)
end
handle_not_implemented (a_description:STRING; res: WGI_RESPONSE_BUFFER )
local
h : EWF_HEADER
do
create h.make
h.put_status (not_implemented)
h.put_content_type ("application/json")
h.put_content_length (a_description.count)
h.add_header ("Date:"+ ((create{HTTP_DATE_TIME_UTILITIES}).now_utc).formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
res.set_status_code (not_implemented)
res.write_headers_string (h.string)
res.write_string (a_description)
end
-- handle_conflic_request_response (a_description:STRING; an_output: HTTPD_SERVER_OUTPUT )
-- local
-- rep: detachable REST_RESPONSE
-- do
-- create rep.make (path)
-- rep.headers.put_status (rep.headers.conflict)
-- rep.headers.put_content_type_application_json
-- rep.set_message (a_description)
-- an_output.put_string (rep.string)
-- rep.recycle
-- end
handle_resource_not_found_response (a_description:STRING; res: WGI_RESPONSE_BUFFER)
local
h : EWF_HEADER
do
create h.make
h.put_status (not_found)
h.put_content_type ("application/json")
h.put_content_length (a_description.count)
h.add_header ("Date:"+ ((create{HTTP_DATE_TIME_UTILITIES}).now_utc).formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
res.set_status_code (not_found)
res.write_headers_string (h.string)
res.write_string (a_description)
end
-- handle_method_not_supported_response (ctx :REST_REQUEST_CONTEXT)
-- local
-- rep: detachable REST_RESPONSE
-- do
-- create rep.make (path)
-- rep.headers.put_status (rep.headers.method_not_allowed)
-- rep.headers.put_content_type_application_json
-- ctx.output.put_string (rep.string)
-- rep.recycle
-- end
end end

View File

@@ -0,0 +1,259 @@
note
description: "Work in progress Common abstraction to handle REST methods"
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_RESOURCE_HANDLER_HELPER[C -> REQUEST_HANDLER_CONTEXT]
inherit
HTTP_STATUS_CODE
feature -- Execute template
execute_methods (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
if req.request_method.same_string ("GET") then
execute_get (ctx,req,res)
elseif req.request_method.same_string ("PUT") then
execute_put (ctx,req,res)
elseif req.request_method.same_string ("DELETE") then
execute_delete (ctx,req,res)
elseif req.request_method.same_string ("POST") then
execute_post (ctx,req,res)
elseif req.request_method.same_string ("TRACE") then
execute_trace (ctx,req,res)
elseif req.request_method.same_string ("OPTIONS") then
execute_options (ctx,req,res)
elseif req.request_method.same_string ("HEAD") then
execute_head (ctx,req,res)
elseif req.request_method.same_string ("CONNECT") then
execute_connect (ctx,req,res)
-- elseif req.request_method.is_valid_extension_method then
-- execute_extension_method (req,res)
end
end
feature-- Method Post
execute_post (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
if req.content_length_value > 0 then
do_post (ctx,req,res)
else
handle_bad_request_response("Bad request, content_lenght empty", req.content_type, res)
end
rescue
handle_internal_server_error("Internal Server Error",req.content_type,res)
end
do_post (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method POST not implemented", req.content_type, res)
end
feature-- Method Put
execute_put (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
if req.content_length_value > 0 then
do_put (ctx,req,res)
else
handle_bad_request_response("Bad request, content_lenght empty", req.content_type, res)
end
rescue
handle_internal_server_error("Internal Server Error", req.content_type,res)
end
do_put (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method PUT not implemented", req.content_type,res)
end
feature -- Method Get
execute_get (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
do_get (ctx,req,res)
rescue
handle_internal_server_error("Internal Server Error",req.content_type,res)
end
do_get (ctx: C;req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method HEAD not implemented", req.content_type, res)
end
feature -- Method DELETE
execute_delete (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
do_delete (ctx,req,res)
rescue
handle_internal_server_error("Internal Server Error",req.content_type,res)
end
do_delete (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method DELETE not implemented", req.content_type, res)
end
feature -- Method CONNECT
execute_connect (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
do_connect (ctx,req,res)
rescue
handle_internal_server_error("Internal Server Error",req.content_type,res)
end
do_connect (ctx: C;req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method CONNECT not implemented", req.content_type, res)
end
feature -- Method HEAD
execute_head (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
do_head (ctx,req,res)
rescue
handle_internal_server_error("Internal Server Error", req.content_type, res)
end
do_head (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method HEAD not implemented", req.content_type, res)
end
feature -- Method OPTIONS
execute_options (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
do_options (ctx,req,res)
rescue
handle_internal_server_error("Internal Server Error",req.content_type,res)
end
do_options (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method OPTIONS not implemented", req.content_type,res)
end
feature -- Method TRACE
execute_trace (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
do_trace (ctx,req,res)
rescue
handle_internal_server_error("Internal Server Error", req.content_type, res)
end
do_trace (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method TRACE not implemented", req.content_type,res)
end
feature -- Method Extension Method
execute_extension_method (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
do_extension_method (ctx,req,res)
rescue
handle_internal_server_error("Internal Server Error",req.content_type,res)
end
do_extension_method (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
handle_not_implemented ("Method EXTENSION METHOD not implemented",req.content_type, res)
end
feature -- Handle responses
handle_bad_request_response (a_description:STRING; content_type : detachable READABLE_STRING_32 ; res: WGI_RESPONSE_BUFFER )
local
h : EWF_HEADER
do
create h.make
h.put_status (bad_request)
if attached content_type as l_content_type then
h.put_content_type (l_content_type)
else
h.put_content_type ("*/*")
end
h.put_content_length (a_description.count)
h.add_header ("Date:"+ get_date)
res.set_status_code (bad_request)
res.write_headers_string (h.string)
res.write_string (a_description)
end
handle_internal_server_error (a_description:STRING; content_type : detachable READABLE_STRING_32 ; res: WGI_RESPONSE_BUFFER )
local
h : EWF_HEADER
do
create h.make
h.put_status (internal_server_error)
if attached content_type as l_content_type then
h.put_content_type (l_content_type)
else
h.put_content_type ("*/*")
end
h.put_content_length (a_description.count)
h.add_header ("Date:"+ get_date)
res.set_status_code (internal_server_error)
res.write_headers_string (h.string)
res.write_string (a_description)
end
handle_not_implemented (a_description:STRING; content_type : detachable READABLE_STRING_32; res: WGI_RESPONSE_BUFFER )
local
h : EWF_HEADER
do
create h.make
h.put_status (not_implemented)
if attached content_type as l_content_type then
h.put_content_type (l_content_type)
else
h.put_content_type ("*/*")
end
h.put_content_length (a_description.count)
h.add_header ("Date:"+ get_date)
res.set_status_code (not_implemented)
res.write_headers_string (h.string)
res.write_string (a_description)
end
handle_resource_not_found_response (a_description:STRING; content_type : detachable READABLE_STRING_32; res: WGI_RESPONSE_BUFFER)
local
h : EWF_HEADER
do
create h.make
h.put_status (not_found)
if attached content_type as l_content_type then
h.put_content_type (l_content_type)
else
h.put_content_type ("*/*")
end
h.put_content_length (a_description.count)
h.add_header ("Date:"+ get_date)
res.set_status_code (not_found)
res.write_headers_string (h.string)
res.write_string (a_description)
end
feature -- Date Utilities
get_date : STRING
do
Result := ((create{HTTP_DATE_TIME_UTILITIES}).now_utc).formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT"
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end