Ocsigen by example, Part 6: POST services


In the previous instalment of this series, the example illustrated the creation of a form for a GET service. This time we'll see a similar example, but one whose service uses POST instead of GET.

GET and POST are just two of the verbs part of the HTTP specification (other verbs are HEAD, PUT, DELETE, etc). The main visible difference is that a GET request encodes the parameters into the URL, whereas POST does so in the body of the request. It is therefore common that web developers pick one or the other based solely on this distinction. This criterion is however fundamentally wrong. GET should be used only for services that do not cause side-effects, and which are typically idempotent. Using GET disregarding this rule may subject your users to possible cross-site request forgery attacks.

As illustrated by the code below, the first thing to notice about POST services is that they require the declaration of the GET fallback service. The reason is because POST services encode parameters in the body of the request, and therefore their parameters are not recorded if the user bookmarks the URL. This requirement may seem annoying at first, but it fits very well with the emphasis on correctness that permeates Ocsigen/Eliom (and the Ocaml ecosystem in general). Another thing to notice is that function Eliom_predefmod.Xhtml.post_form (the POST counterpart of Eliom_predefmod.Xhtml.get_form) also requires the GET parameters for the service.

When trying this example, I suggest you reload the "coucou" URL after its first invocation via the form. You will see that upon the second invocation the fallback page is displayed, because no POST parameters were specified. (Depending on the browser, simply hitting "reload" will resend the POST parameters; therefore, select the URL and press ENTER for a clean invocation).

(************************************************************************)
(* Services with POST parameters. *)
(************************************************************************)

open XHTML.M
open Eliom_parameters


(************************************************************************)
(* This service is just the fallback for the "coucou" service, and
is never meant to be directly invoked. Eliom forces one to declare
these fallbacks because browsers bookmark only the GET parameters
and not the POST ones. There is therefore a chance the service is
invoked without any POST parameters.
*)

let fallback_handler _ () () =
Lwt.return
(html
(head (title (pcdata "Fallback")) [])
(body [p [pcdata "You've invoked the fallback service for coucou"]]))

let fallback_service =
Eliom_predefmod.Xhtml.register_new_service
~path: ["coucou"]
~get_params: Eliom_parameters.unit
fallback_handler


(************************************************************************)
(* Service "coucou" takes no GET parameters and one POST parameter.
Note that during the service registration the service path is not
indicated, since it will be the same as the fallback's. The same
goes for the GET parameters.
*)

let coucou_handler _ () foo =
Lwt.return
(html
(head (title (pcdata "Coucou")) [])
(body [p [pcdata ("The POST parameter is " ^ foo)]]))

let coucou_service =
Eliom_predefmod.Xhtml.register_new_post_service
~fallback: fallback_service
~post_params: (Eliom_parameters.string "foo")
coucou_handler


(************************************************************************)
(* This function creates a form for the "coucou" service. Note that
proper XHTML forms should be divided into fieldsets, and that any
input widget should have a matching label, as indicated via its ID.
*)

let coucou_form enter_foo =
let enter_foo_label = "enter_foo"
in [
fieldset
[
label ~a:[a_for enter_foo_label] [pcdata "String foo:"];
Eliom_predefmod.Xhtml.string_input ~a:[a_id enter_foo_label] ~input_type:`Text ~name:enter_foo ();
Eliom_predefmod.Xhtml.string_input ~input_type:`Submit ~value:"Click" ()
]
]



(************************************************************************)
(* The "main" service creates a form for the "coucou" POST service.
*)

let main_handler sp () () =
Lwt.return
(html
(head (title (pcdata "Main")) [])
(body [
p [pcdata "Please fill in this form:"];
Eliom_predefmod.Xhtml.post_form coucou_service sp coucou_form ()
]))

let main_service =
Eliom_predefmod.Xhtml.register_new_service
~path: [""]
~get_params: Eliom_parameters.unit
main_handler

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this entry.
Comments
  • No comments exist for this entry.
Leave a comment

Submitted comments will be subject to moderation before being displayed.

 Enter the above security code (required)

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.