diff --git a/examples/form/README.md b/examples/form/README.md
new file mode 100644
index 00000000..a79ed339
--- /dev/null
+++ b/examples/form/README.md
@@ -0,0 +1,20 @@
+This example demonstrates the use of the `wsf_html` library for web form handling.
+
+To simplify the code, it is also using the `WSF_RESPONSE.send (WSF_RESPONSE_MESSAGE)`,
+thus no need to write the expected http header here.
+
+The current code is a web interface form, returning html page as response.
+
+notes:
+* It is not using the `WSF_ROUTER` component to keep the example as simple as possible.
+* It is also possible to use the `WSF_REQUEST.form_parameter (...)` directly,
+ but WSF_FORM provides advanced processing.
+* For a CRUD REST API, you can also use `WSF_FORM` to analyze the incoming POST values,
+ however the html generation may be too verbose for a simple REST api.
+
+warning: depending on your system and connector, you may need to use `WSF_REQUEST.set_uploaded_file_path (...)`
+ to tell the server where to store the temporary uploaded files.
+ (this should be a directory with write permission for the server).
+ For that
+ - inherit from WSF_REQUEST_EXPORTER
+ - in `execute` add at the beginning the call `request.set_uploaded_file_path (path-to-wanted-directory)`
diff --git a/examples/form/application.e b/examples/form/application.e
new file mode 100644
index 00000000..84d6d540
--- /dev/null
+++ b/examples/form/application.e
@@ -0,0 +1,29 @@
+note
+ description: "See description of {APPLICATION_EXECUTION}"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ APPLICATION
+
+inherit
+ WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
+ redefine
+ initialize
+ end
+
+create
+ make_and_launch
+
+feature {NONE} -- Initialization
+
+ initialize
+ -- Initialize current service.
+ do
+ -- Specific to `standalone' connector (the EiffelWeb server).
+ -- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
+ set_service_option ("port", 9090)
+ import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("server.ini"))
+ end
+
+end
diff --git a/examples/form/application_execution.e b/examples/form/application_execution.e
new file mode 100644
index 00000000..c837ba24
--- /dev/null
+++ b/examples/form/application_execution.e
@@ -0,0 +1,523 @@
+note
+ description : "[
+ This example demonstrates the use of the `wsf_html` library for web form handling.
+
+ To simplify the code, it is also using the `WSF_RESPONSE.send (WSF_RESPONSE_MESSAGE)`,
+ thus no need to write the expected http header here.
+
+ The current code is a web interface form, returning html page as response.
+
+ notes:
+ * It is not using the `WSF_ROUTER` component to keep the example as simple as possible.
+ * It is also possible to use the `WSF_REQUEST.form_parameter (...)` directly,
+ but WSF_FORM provides advanced processing.
+ * For a CRUD REST API, you can also use `WSF_FORM` to analyze the incoming POST values,
+ however the html generation may be too verbose for a simple REST api.
+
+ warning: depending on your system and connector, you may need to use `WSF_REQUEST.set_uploaded_file_path (...)`
+ to tell the server where to store the temporary uploaded files.
+ (this should be a directory with write permission for the server).
+ For that
+ - inherit from WSF_REQUEST_EXPORTER
+ - in `execute` add at the beginning the call `request.set_uploaded_file_path (path-to-wanted-directory)`
+
+ ]"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ APPLICATION_EXECUTION
+
+inherit
+ WSF_EXECUTION
+
+create
+ make
+
+feature -- Basic operations
+
+ execute
+ local
+ msg: WSF_RESPONSE_MESSAGE
+ do
+ if request.path_info.same_string ("/form") then
+ if request.is_post_request_method then
+ msg := form_submission_message
+ elseif request.is_get_request_method then
+ msg := form_message
+ else
+ create {WSF_METHOD_NOT_ALLOWED_RESPONSE} msg.make (request)
+ end
+ else
+ msg := welcome_message --| Alternative: return a not found message.
+ end
+ response.send (msg)
+ end
+
+ welcome_message: WSF_HTML_PAGE_RESPONSE
+ local
+ l_html: STRING
+ do
+ Result := new_html_page_message ("Welcome")
+
+ create l_html.make_empty
+ l_html.append ("
")
+ Result.set_body (l_html)
+ end
+
+ form_message: WSF_HTML_PAGE_RESPONSE
+ local
+ f: WSF_FORM
+ l_html: STRING
+ do
+ Result := new_html_page_message ("Form")
+ create l_html.make_empty
+ l_html.append ("
Fill the form
%N")
+
+ -- Build the Web form, and then generate the html into `l_html`
+ f := new_form
+ f.append_to_html (wsf_theme, l_html)
+ Result.set_body (l_html)
+ end
+
+ form_submission_message: WSF_HTML_PAGE_RESPONSE
+ local
+ f: WSF_FORM
+ l_html: STRING
+ do
+ create l_html.make_empty
+ Result := new_html_page_message ("Form")
+
+ -- Build the empty Web form
+ f := new_form
+ -- And fill it using the `request: WSF_REQUEST` object.
+ f.process (request, Void, Void)
+ -- Get the filled analyzed data from `f.last_data: WSF_FORM_DATA`
+ if attached f.last_data as fd then
+ if attached fd.errors as lst then
+ -- The web form has validation errors
+ l_html.append ("
Fill the form
%N")
+
+ fd.apply_to_associated_form
+ across
+ lst as ic
+ loop
+ if attached {WSF_FORM_FIELD} ic.item.field as l_field then
+ l_html.append ("
ERROR on field [" + l_field.name + "]
")
+ end
+ end
+ f.append_to_html (wsf_theme, l_html)
+ elseif
+ attached fd.string_item (field_id_uuid) as l_uuid and then
+ attached fd.string_item (field_id_first_name) as l_firstname and then
+ attached fd.string_item (field_id_last_name) as l_lastname
+
+ then
+ -- No validation errors
+ -- and we have the expected values.
+
+ check
+ -- see `set_is_required (True)` in `new_form`.
+ required_value_set: not l_firstname.is_whitespace and not l_lastname.is_whitespace
+ end
+ --| Process the value to store in database, or anything ...
+ --| here, we just display the value in the page.
+ l_html.append ("
Form: results
")
+ l_html.append ("
Thank you %"" + l_firstname + " " + l_lastname + "%" for your submission ("+ l_uuid.as_string_8 +").
")
+ l_html.append ("
")
+ -- Use the html encoder to append the firstname and lastname that could be unicode or contain reserved html characters.
+
+ -- Required values.
+ l_html.append ("
first name: " + Result.html_encoded_string (l_firstname) + "
")
+ l_html.append ("
last name: " + Result.html_encoded_string (l_lastname) + "
")
+
+ -- Optional values
+ if attached fd.string_item (field_id_birthday) as l_birthday and then not l_birthday.is_whitespace then
+ l_html.append ("
birthday: " + l_birthday + "
")
+ end
+ if attached fd.string_item (field_id_email) as l_email and then not l_email.is_whitespace then
+ l_html.append ("
email: " + l_email + "
")
+ end
+ if attached fd.string_item (field_id_priority) as l_priority then
+ l_html.append ("
priority: " + l_priority.to_string_8 + "
")
+ end
+ if attached fd.table_item (field_id_tags) as l_tags then
+ l_html.append ("
tags: ")
+ across
+ l_tags as ic
+ loop
+ if attached {WSF_STRING} ic.item as p_tag then
+ l_html.append (Result.html_encoded_string (p_tag.value))
+ l_html.append (" ")
+ end
+ end
+ l_html.append ("
")
+ end
+ if attached fd.string_item (field_id_month) as l_month then
+ l_html.append ("
")
+ end
+ if attached fd.string_item (field_id_comment) as l_comment then
+ l_html.append ("
comment:
" + Result.html_encoded_string (l_comment) + "
")
+ end
+
+ -- WARNING for uploaded file, the form will send the file content only if
+ -- `{WSF_FORM}.set_multipart_form_data_encoding_type` is used.
+ -- otherwise we get only the "filename"...
+ if attached {WSF_UPLOADED_FILE} fd.item (field_id_attached_file) as l_uploaded_file then
+ l_html.append ("
Attached file %"" + Result.html_encoded_string (l_uploaded_file.filename) + "%" (size=" + l_uploaded_file.size.out + ") ")
+ if attached l_uploaded_file.tmp_path as l_tmp_path then
+ l_html.append (" saved as %"")
+ l_html.append (Result.html_encoded_string (l_tmp_path.name))
+ l_html.append ("%"")
+ end
+ l_html.append ("
")
+ elseif attached fd.string_item (field_id_attached_file) as l_file then
+ l_html.append ("