Ocsigen

This is a preliminary version of the documentation. Help us to improve it by filling tickets. We are looking for native english speakers to proof read the documentation. Contact us!

1. The basics: main services, parameters, forms, cooperative programming

Base principles

Unlike many other Web programming techniques (CGI, PHP, ...), with Eliom, you don't write one file for each URL, but a caml module (cmo or cma) for the whole Web site.

The Eliomservices module allows to create new entry points to your Web site, called services. Services are usually attached to an URL and usually generate a Web page. They are represented by OCaml values, on which you must register a function that will generate a page. There are several ways to creates pages for Eliom. This tutorial is mainly using Eliompredefmod.Xhtml, a module allowing to register xhtml pages statically typed using OCaml's polymorphic variants. The XHTML.M module defines functions to construct xhtml pages using that type system. As the Eliompredefmod.Xhtml redefines some functions of XHTML.M, open the modules in this order:


open Lwt
open XHTML.M
open Eliomservices
open Eliomparameters
open Eliomsessions
open Eliompredefmod.Xhtml

Lwt (lightweight threads) is the cooperative thread library used by Ocsigen (see later).

Here is an example showing how to create a new service and register a page created with XHTML.M. Use the function Eliompredefmod.Xhtml.register_new_service:


let coucou = 
  register_new_service 
    ~path:["coucou"]
    ~get_params:unit
    (fun _ () () -> 
      return 
        (html
           (head (title (pcdata "")) [])
           (body [h1 [pcdata "Hallo!"]])))

The same, written with fully qualified values:


let coucou = 
  Eliompredefmod.Xhtml.register_new_service 
    ~path:["coucou"]
    ~get_params:Eliomparameters.unit
    (fun _ () () -> 
      Lwt.return 
        (XHTML.M.html
          (XHTML.M.head (XHTML.M.title (XHTML.M.pcdata "")) [])
          (XHTML.M.body [XHTML.M.h1 [XHTML.M.pcdata "Hallo!"]])))

As you can see, return is a function from Lwt. Use it as this for now, and see later for more advanced use.

Now you can compile your file (here tutorial.ml) by doing:

ocamlc -I /path_to/ocsigen/ -c tutorial.ml

Replace /path_to/ocsigen/ by the directory where Ocsigen libraries are installed (that contains eliom.cma, staticmod.cmo, etc.), usually something like /usr/lib/ocaml/3.09.3/ocsigen or /usr/local/lib/ocaml/3.09.3/ocsigen or /opt/godi/lib/ocaml/site-lib/ocsigen.

Add the following lines to Ocsigen's config file (usually /etc/ocsigen/ocsigen.conf):

<host> <site dir="examples"> <eliom module="/path_to/tutoeliom.cmo" /> </site> </host>

Then run ocsigen. You should see your page at url http://your_server/examples/coucou. See this example here.

NB: See the default config file to see how to set the port on which your server is running, the user who runs it, the path of the log files, etc.

Here is a sample Makefile for your modules.

Static typing of XHTML with XHTML.M

Typing of xhtml with XHTML.M and Eliompredefmod.Xhtml is very strict and compels you to respect xhtml 1.1 standard (with some limitations). For example if you write:

(html
   (head (title (pcdata "")) [])
   (body [pcdata "Hallo"]))

You have the following error message:

This expression has type ([> `PCDATA ] as 'a) XHTML.M.elt
but is here used with type 
([< XHTML.M.block ] as 'b) XHTML.M.elt
Type 'a is not compatible with type
'b =
  [< `Address | `Blockquote | `Del | `Div | `Dl | `Fieldset
   | `Form | `H1 | `H2 | `H3 | `H4 | `H5 | `H6 | `Hr | `Ins
   | `Noscript | `Ol | `P | `Pre | `Script | `Table | `Ul ]

'b is the type of block tags (only tags allowed inside <body>), but PCDATA (i.e. raw text) is not a block tag.

In XHTML, some tags cannot be empty. For example <table> must contains at least one row. To enforce this, the XHTML.M.table function takes two parameters: the first one is the first row, the second one is a list containing all the other rows. (same thing for <tr> <form> <dl> <ol> <ul> <dd> <select> ...) This forces the user to handle the empty list case specially and thus make the output conform to the DTD.

A more detailed introduction to XHTML.M is available here. Take a quick look at it before continuing this tutorial.

Alternate syntax

If you prefer using a syntax closer to html, you can write:


let coucou1 = 
  Eliompredefmod.Xhtml.register_new_service 
    ~path:["coucou1"]
    ~get_params:Eliomparameters.unit
    (fun _ () () -> 
      return
        << <html>
             <head><title></title></head>
             <body><h1>Coucou</h1></body>
           </html> >>)

To compile this syntax, you need a camlp4 syntax extension:

ocamlc -I /path_to/ocsigen/ -pp "camlp4o /path_to/ocsigen/xhtmlsyntax.cma -loc loc" -c tutorial.ml

(Replace /path_to/ocsigen/ by the directory where ocsigen is installed). See this example here.

As the syntax extension is using the same typing system as XHTML.M, You can mix the two syntaxes (see later).

Warning: The two syntaxes are not equivalent for typing. Using the syntax extension will do less verifications. For example the following code is accepted but not valid with respect to the xhtml's dtd (because <head> must contain a title):

We recommand to use preferably the functions from XHTML.M, as you will (almost) always get valid xhtml. Use the syntax extension for example to enclose already created pieces of html, and verify the validity of your pages with the W3C validator.

More info on XHTML.M.

More info on the syntax extension.

Eliom and OCamlDuce

If OCamlDuce is installed on your system, it is now possible to use it instead of XHTML.M and Eliomparameters.Xhtml to typecheck your pages. You get a stronger type checking and more flexibility (easier to use other XML types, easier to parse incoming XML data, etc.).

To use it, make sure that you have Eliom compiled with OCamlDuce support. Then dynlink ocamlduce.cma and eliomduce.cma from the configuration file (after eliom.cma). Then use Eliomduce.Xhtml instead of Eliompredefmod.Xhtml to register your pages.

Here is an example:

open Lwt

let s =
  Eliomduce.Xhtml.register_new_service 
    ~path:[""]
    ~get_params:unit
    (fun sp () () ->
      return
        {{ <html>
             [<head> [<title> ""]
              <body> [<h1> "This page has been type checked by OCamlDuce"]] }}) 

Eliompredefmod.HtmlText

If you want to register untyped (text) pages, use the functions from Eliompredefmod.HtmlText, for example Eliompredefmod.Text.register_new_service. Example:


let coucoutext = 
  Eliompredefmod.HtmlText.register_new_service 
    ~path:["coucoutext"]
    ~get_params:Eliomparameters.unit
    (fun sp () () -> 
      return
        ("<html>n'importe quoi "^
         (Eliompredefmod.HtmlText.a coucou sp "clic" ())^
         "</html>"))

Try it.

More examples

Services registered with register_new_service are available for all users. We call them public services.

Page generation may have side-effects:


let count = 
  let next =
    let c = ref 0 in
      (fun () -> c := !c + 1; !c)
  in
  register_new_service 
    ~path:["count"]
    ~get_params:unit
    (fun _ () () -> 
      return
        (html
         (head (title (pcdata "counter")) [])
         (body [p [pcdata (string_of_int (next ()))]])))

See this example here.

As usual in OCaml, you can forget labels when the application is total:


let hello = 
  register_new_service 
    ["dir";"hello"]  (* the url dir/hello *)
    unit
    (fun _ () () ->
      return
        (html
         (head (title (pcdata "Hello")) [])
         (body [h1 [pcdata "Hello"]])))

See this example here.

Warning about pathes: ["foo";"bar"] is not equivalent to ["foo/bar"]. In the latter, the "/" will be encoded in the URL.

The last example shows how to define the default page for a directory. (Note that ["rep";""] is equivalent to ["rep";"index"], because some browsers do not behave well with empty links.)


let default = register_new_service ["rep";""] unit
  (fun _ () () -> 
    return
     (html
      (head (title (pcdata "")) [])
      (body [p [pcdata "default page. rep is redirected to rep/"]])))

See default.

Parameters

Typed parameters

The parameter labeled get_params indicates the type of GET parameters for the page (that is, parameters present in the URL). unit means that the page does not take any GET parameter.

Functions implementing services are called service handlers. They take three parameters. The first one has type Eliomsessions.server_params and corresponds to server parameters (user-agent, ip, current-url, etc. - see later in that section for examples of use), the second one is for GET parameters (that is, parameters in the URL) and the third one for POST parameters (parameters in the body of the HTTP request).

Here is an example of a service with GET parameters:


let writeparams _ (i1, (i2, s1)) () =  
  return
   (html
    (head (title (pcdata "")) [])
    (body [p [pcdata "You sent: ";
              strong [pcdata (string_of_int i1)];
              pcdata ", ";
              strong [pcdata (string_of_int i2)];
              pcdata " and ";
              strong [pcdata s1]]]))

let coucou_params = register_new_service 
    ~path:["coucou"]
    ~get_params:(int "i" ** (int "ii" ** string "s"))
    writeparams


Note that the URLs of coucou and coucou_params differ only by parameters. Url http://your_server/examples/coucou will run the first one,
http://your_server/examples/coucou?i=42&ii=17&s=krokodile will run the second one.
If i is not an integer, the server displays an error-message (try to change the value in the URL).
Here, int, string and are functions defined in the Eliomparameters module.
Warning: The infix function ( ) is to be used to construct pairs (not tuples).

The following examples shows how to create a service with "suffix" service (taking the end of the URL as a parameter, as wikis do very often) and how to get server information:


let uasuffix = 
  register_new_service 
    ~path:["uasuffix"]
    ~get_params:(suffix (int "year" ** int "month"))
    (fun sp (year, month) () -> 
      return
       (html
        (head (title (pcdata "")) [])
        (body
           [p [pcdata "The suffix of the url is ";
               strong [pcdata ((string_of_int year)^"/"
                               ^(string_of_int month))];
               pcdata ", your user-agent is ";
               strong [pcdata (Eliomsessions.get_user_agent sp)];
               pcdata ", your IP is ";
               strong [pcdata (Eliomsessions.get_ip sp)]]])))

This service will answer to URLs like http://.../uasuffix/2000/11.

See uasuffix

Suffix parameters have names, because we can create forms towards these services. uasuffix/2000/11 is equivalent to uasuffix/?year=2000&month=11.

suffix_prod allows to take both a suffix and other parameters.
all_suffix allows to take the end of the suffix as string list.


let isuffix = 
  register_new_service 
    ~path:["isuffix"] 
    ~get_params:(suffix_prod (int "suff" ** all_suffix "endsuff") (int "i"))
    (fun sp ((suff, endsuff), i) () -> 
      return
       (html
        (head (title (pcdata "")) [])
        (body
           [p [pcdata "The suffix of the url is ";
               strong [pcdata (string_of_int suff)];
               pcdata " followed by ";
               strong [pcdata (Extensions.string_of_url_path endsuff)];
               pcdata " and i is equal to ";
               strong [pcdata (string_of_int i)]]])))

See isuffix.

The following example shows how to use your own types:


type mysum = A | B
let mysum_of_string = function
  | "A" -> A
  | "B" -> B
  | _ -> raise (Failure "mysum_of_string")
let string_of_mysum = function
  | A -> "A"
  | B -> "B"

let mytype = 
  Eliompredefmod.Xhtml.register_new_service 
    ~path:["mytype"]
    ~get_params:
      (Eliomparameters.user_type mysum_of_string string_of_mysum "valeur")
    (fun _ x () -> 
      let v = string_of_mysum x in 
      return
        (html
         (head (title (pcdata "")) [])
         (body [p [pcdata (v^" is valid. Now try with another value.")]])))

See mytype.

Untyped parameters

If you want a service that answers to request with any parameters, use the Eliomparameters.any value. The service will get an association list of strings. Example:


let raw_serv = register_new_service 
    ~path:["any"]
    ~get_params:Eliomparameters.any
  (fun _ l () ->
    let ll = 
      List.map 
        (fun (a,s) -> << <strong>($str:a$, $str:s$)</strong> >>) l 
    in  
    return
     << <html>
          <head><title></title></head>
          <body>
          <p>
            You sent: 
            $list:ll$
          </p>
          </body>
        </html> >>)

Try raw_serv.

Catching errors

You can catch typing errors of parameters and write your own error messages using the optional parameter error_handler. Example:



let catch = register_new_service
    ~path:["catch"]
    ~get_params:(int "i")
    ~error_handler:(fun sp l -> 
      return
        (html
         (head (title (pcdata "")) [])
         (body [p [pcdata ("i is not an integer.")]])))
    (fun _ i () -> 
      let v = string_of_int i in 
      return
        (html
           (head (title (pcdata "")) [])
           (body [p [pcdata ("i is an integer: "^v)]])))

error_handler takes as parameters the usual sp, and a list of pairs (n,ex), where n is the name of the wrong parameter, and ex is the exception that has been raised while parsing its value.

See catch (change the value of the parameter).

To create a link (<a>), use the function Eliompredefmod.Xhtml.a (or Eliomduce.Xhtml.a, etc), as in these examples:


let links = register_new_service ["rep";"links"] unit
 (fun sp () () -> 
   return
    (html
     (head (title (pcdata "Links")) [])
     (body 
       [p
        [Eliompredefmod.Xhtml.a coucou sp [pcdata "coucou"] (); br ();
         Eliompredefmod.Xhtml.a hello sp [pcdata "hello"] (); br ();
         Eliompredefmod.Xhtml.a default sp 
           [pcdata "default page of the dir"] (); br ();
         Eliompredefmod.Xhtml.a uasuffix sp 
           [pcdata "uasuffix"] (2007,06); br ();
         Eliompredefmod.Xhtml.a coucou_params sp 
           [pcdata "coucou_params"] (42,(22,"ciao")); br ();
         Eliompredefmod.Xhtml.a raw_serv sp 
           [pcdata "raw_serv"] [("sun","yellow");("sea","blue and pink")]; br ();
         Eliompredefmod.Xhtml.a
           (new_external_service
              ~prefix:"http://fr.wikipedia.org"
              ~path:["wiki";""]
              ~get_params:(suffix (all_suffix "suff"))
              ~post_params:unit ()) 
           sp
           [pcdata "OCaml on wikipedia"]
           ["OCaml"]; br ();
         XHTML.M.a
           ~a:[a_href (uri_of_string "http://en.wikipedia.org/wiki/OCaml")]
           [pcdata "OCaml on wikipedia"]
       ]])))


See links.

If you open Eliompredefmod.Xhtml after XHTML.M, Eliompredefmod.Xhtml.a will mask XHTML.M.a. Thus you can avoid to write fully qualified values most of the time.

Eliompredefmod.Xhtml.a takes as first parameter the service you want to link to. Note that to create a (relative) link we need to know the current URL. That's why the function a takes sp as second parameter.

The third parameter is the text of the link. The last parameter is for GET parameters you want to put in the link. The type of this parameter and the name of GET parameters depend on the service you link to.

The links to Wikipedia shows how to define an external service (here it uses a suffix URL). For an external service without parameters, you can use the low level function XHTML.M.a, if you don't want to create an external service explicitely. Note that the path must be a list of strings. Do not write ["foo/bar"], but ["foo";"bar"], otherwise, the "/" will be encoded in the URL.

If you want to create (mutually or not) recursive pages, first create the service using Eliomservices.new_service, then register it in the table using (for example) Eliompredefmod.Xhtml.register:


let linkrec = Eliomservices.new_service ["linkrec"] unit ()

let _ = Eliompredefmod.Xhtml.register linkrec 
    (fun sp () () -> 
      return
       (html
        (head (title (pcdata "")) [])
        (body [p [a linkrec sp [pcdata "click"] ()]])))


See linkrec.

The server will fail on start up if there are any unregistered services.

Forms

Forms towards services

The function Eliompredefmod.Xhtml.get_form allows to create a form that uses the GET method (parameters in the URL). It works like Eliompredefmod.Xhtml.a but takes as parameter a function that creates the form from parameters names.


let create_form = 
  (fun (number_name, (number2_name, string_name)) ->
    [p [pcdata "Write an int: ";
        Eliompredefmod.Xhtml.int_input ~input_type:`Text ~name:number_name ();
        pcdata "Write another int: ";
        Eliompredefmod.Xhtml.int_input ~input_type:`Text ~name:number2_name ();
        pcdata "Write a string: ";
        Eliompredefmod.Xhtml.string_input ~input_type:`Text ~name:string_name ();
        Eliompredefmod.Xhtml.string_input ~input_type:`Submit ~value:"Click" ()]])

let form = register_new_service ["form"] unit
  (fun sp () () -> 
     let f = Eliompredefmod.Xhtml.get_form coucou_params sp create_form in 
     return
       (html
         (head (title (pcdata "")) [])
         (body [f])))

See the function form in action.

Note that if you want to use typed parameters, you cannot use functions like XHTML.M.input to create your forms (but for parameters defined with Eliomparameters.any, see later). Indeed, parameter names are typed to force them be used properly. In our example, number_name has type int param_name and must be used with int_input (or other widgets), whereas string_name has type string param_name and must be used with string_input (or other widgets). All functions for creating form widgets are detailed here.

For untyped forms, you may use functions from XHTML.M (or OCamlDuce's syntax, or whatever syntax you are using) or functions whose name is prefixed by "raw_". Here is a form linking to our (untyped) service raw_serv.


let raw_form = register_new_service 
    ~path:["anyform"]
    ~get_params:unit
    (fun sp () () ->
      return
        (html
           (head (title (pcdata "")) [])
           (body 
              [h1 [pcdata "Any Form"];
               Eliompredefmod.Xhtml.get_form raw_serv sp 
                 (fun () ->
                   [p [pcdata "Form to raw_serv: ";
                       Eliompredefmod.Xhtml.raw_input ~input_type:`Text ~name:"plop" ();
                       Eliompredefmod.Xhtml.raw_input ~input_type:`Text ~name:"plip" ();
                       Eliompredefmod.Xhtml.raw_input ~input_type:`Text ~name:"plap" ();
                       Eliompredefmod.Xhtml.string_input ~input_type:`Submit ~value:"Click" ()]])
                ])))

Try this form.

POST parameters

By default Web page parameters are passed in the URL (GET parameters). A web page may also expect POST parameters (that is, parameters that are not in the URL but in the body of the HTTP request). Use this if you don't want the user to be able to bookmark the URL with parameters, for example if you want to post some data that will change the state of the server (payment, database changes, etc). When designing a Web site, think carefully about the choice between GET or POST method for each service!

When you register a service with POST parameters, you must register before a service (fallback) without these parameters (for example that will answer if the page is reloaded without the hidden parameters, or if it is bookmarked).


let no_post_param_service = 
  register_new_service 
    ~path:["post"]
    ~get_params:unit
    (fun _ () () -> 
      return
        (html
         (head (title (pcdata "")) [])
         (body [h1 [pcdata 
                      "Version of the page without POST parameters"]])))
    
let my_service_with_post_params = 
  register_new_post_service
    ~fallback:no_post_param_service
    ~post_params:(string "value")
    (fun _ () value -> 
      return
        (html
         (head (title (pcdata "")) [])
         (body [h1 [pcdata value]])))

Services may take both GET and POST parameters:


let get_no_post_param_service = 
  register_new_service 
    ~path:["post2"]
    ~get_params:(int "i")
    (fun _ i () -> 
      return
        (html
         (head (title (pcdata "")) [])
         (body [p [pcdata "No POST parameter, i:";
                   em [pcdata (string_of_int i)]]])))

let my_service_with_get_and_post = register_new_post_service 
  ~fallback:get_no_post_param_service
  ~post_params:(string "value")
  (fun _ i value -> 
    return
      (html
         (head (title (pcdata "")) [])
         (body [p [pcdata "Value: ";
                   em [pcdata value];
                   pcdata ", i: ";
                   em [pcdata (string_of_int i)]]])))

POST forms

To create a POST form, use the Eliompredefmod.Xhtml.post_form function. It is similar to Eliompredefmod.Xhtml.get_form with an additional parameter for the GET parameters you want to put in the URL (if any). Here form2 is a page containing a form to the service post (using XHTML.M's functions) and form3 (defined using the syntax extension) contains a form to post2, with a GET parameter. form4 is a form to an external page.


let form2 = register_new_service ["form2"] unit
  (fun sp () () -> 
     let f =
       (Eliompredefmod.Xhtml.post_form my_service_with_post_params sp
          (fun chaine -> 
            [p [pcdata "Write a string: ";
                string_input ~input_type:`Text ~name:chaine ()]]) ()) in
     return
       (html
         (head (title (pcdata "form")) [])
         (body [f])))

let form3 = register_new_service ["form3"] unit
  (fun sp () () ->
     let f  = 
       (Eliompredefmod.Xhtml.post_form my_service_with_get_and_post sp
          (fun chaine -> 
            <:xmllist< <p> Write a string: 
                    $string_input ~input_type:`Text ~name:chaine ()$ </p> >>)
          222) in 
     return
       << <html>
            <head><title></title></head>
            <body>$f$</body></html> >>)

let form4 = register_new_service ["form4"] unit
  (fun sp () () ->
     let f  = 
       (Eliompredefmod.Xhtml.post_form
          (new_external_service 
             ~prefix:"http://www.petizomverts.com"
             ~path:["zebulon"]
             ~get_params:(int "i")
             ~post_params:(string "chaine") ()) sp
          (fun chaine -> 
            <:xmllist< <p> Write a string: 
                     $string_input ~input_type:`Text ~name:chaine ()$ </p> >>)
          222) in 
     return
       (html
        (head (title (pcdata "form")) [])
        (body [f])))

See the urls: post without parameter, post2 without POST parameter, form2, form3, form4.

Threads

Remember that a Web site written with Eliom is an OCaml application. This application must be able to handle several requests at the same time, if one of the requests takes time. To make this possible, Ocsigen is using cooperative threads, implemented in monadic style by Jérôme Vouillon (Lwt module), which make them really easy to use.

Take time to read the documentation about Lwt right now if you want to understand the foloowing of this tutorial.

As it does not cooperate, the following page will stop the server for 5 seconds. No one will be able to do a request during this delay:

let looong =
  register_new_service
    ~path:["looong"]
    ~get_params:unit
    (fun sp () () ->
      Unix.sleep 5;
      return
        (html
          (head (title (pcdata "")) [])
          (body [h1 [pcdata "Ok now, you can read the page."]])))

To solve this problem, use a cooperative version of sleep:


let looong = 
  register_new_service 
    ~path:["looong"]
    ~get_params:unit
    (fun sp () () -> 
      Lwt_unix.sleep 5.0 >>= fun () ->
      return
        (html
          (head (title (pcdata "")) [])
          (body [h1 [pcdata 
                   "Ok now, you can read the page."]])))

If you want to use, say, a database library that is not written in cooperative way, but is thread safe for preemptive threads, use the Preemptive module to detach the computation. In the followinf example, we simulate the request by a call to Unix.sleep:


let looong2 = 
  register_new_service 
    ~path:["looong2"]
    ~get_params:unit
    (fun sp () () -> 
      Preemptive.detach Unix.sleep 5 >>= fun () ->
      return
        (html
          (head (title (pcdata "")) [])
          (body [h1 [pcdata 
                   "Ok now, you can read the page."]])))

See looong2.

The big picture

You now have the minimum knowledge to write basic Web sites with Eliom: typing of pages, creation of services, parameters, forms and database acces using Lwt (and possibly Preemptive.detach). Here is a summary of all other concepts introduced by Eliom. They will enable you to program easily more complex behaviours. They will be developped in the following of this tutorial.

Different kinds of services

Before beginning the implementation, think about the URLs you want to create as entry points to your Web site, and the services you want to provide.

Services we used so far are called main services. Actually, Eliom uses three kinds of services:

Main services
are the main entry points of your sites. Created by new_service or new_post_service. They correspond to the public URLs of your Web site, and will last forever.
(Attached) coservices
are services that share their location (URL) with a main service (fallback). They are distinguished from that main service using a special parameter (added automatically by Eliom). They are often created dynamically for one user (often in the session table), depending on previous interaction during the session. They often disappear after a timeout letting the fallback answer afterwards. Another use of (POST) coservices is to particularize one button but not the page it leads to (like the disconnect button in the example of sessions with actions below).
Non-attached coservices
are coservices that are not attached to a particular URL. A link towards a non-attached coservice will go to the current URL (with special parameters allowing to recognize the coservice). It is usefull when you want the same link or form on several pages (for example a connection box) but you don't want to go to another URL. Non-attached coservice are ofetn used with actions (see below).====GET or POST? Each of these services has both a version with GET parameters and a version with POST parameters.

POST and GET parameters is not equivalent, and you must be very carefull if you want one or the other.
GET parameters are the parameters you see in the URL (for example http://your_server/examples/coucou?i=42&ii=17&s=krokodile). They are created by browsers if you tell forms to use the GET method, or written directly in the URL.
Parameters are sent by browsers in the body of the HTTP request when the form is using the POST method. That's the only solution if you want to send files with your request.

Remember that only pages without POST parameters are bookmarkable. Use GET parameters if you want the user be able to come back to the URL later or to write the URL manually.
Use POST parameters when you want a different behaviour between the first click and a reload of the page. Usually sending POST parameters triggers an action on server side (like a paiement, or adding something in a database), and you don't want it to succeed several times if the page is reloaded or bookmarked.

Data returned by services

Services can send several types of data, using these different modules:

ServicesAttached coservicesNon attached coservices
Eliompredefmod.Xhtmlallows to register functions that generate xhtml pages statically checked using polymorphic variant types. You may use constructor functions from XHTML.M or a syntax extension close to the standard xhtml syntax.
Eliompredefmod.Blocksallows to register functions that generate a portion of page (content of body tag) using XHTML.M or the syntax extension. (usefull for XMLHttpRequest requests for example).
Eliomduce.Xhtmlallows to register functions that generate xhtml pages statically checked using OCamlduce. Typing is more strict, but you need a modified version of the OCaml compiler (OCamlduce).
Eliompredefmod.HtmlTextAllows to register functions that generate text html pages, without any typechecking of the content. The content type sent by the server is "text/html".
Eliompredefmod.CssTextAllows to register functions that generate CSS pages, without any typechecking of the content. The content type sent by the server is "text/css".
Eliompredefmod.TextAllows to register functions that generate text pages, without any typechecking of the content. The services return a pair of strings. The first one is the content of the page, the second one is the content type.
Eliompredefmod.Actionsallows to register actions, that is functions that do not generate any page. The URL is reloaded after the action.
Eliompredefmod.Unitis like Eliompredefmod.Actions but the URL is not reloaded after the action.
Eliompredefmod.Redirectionsallows to register HTTP permanent redirections. You register the URL of the page you want to redirect to. The browser will get a 301 code in answer and redo the request to the new URL.
Eliompredefmod.TempRedirectionsallows to register HTTP temporary redirections. You register the URL of the page you want to redirect to. The browser will get a 302 code in answer and redo the request to the new URL.
Eliompredefmod.Filesallows to register services that send files
Eliompredefmod.Anyallows to register services that can choose what they send, for example an xhtml page or a file, depending on some situation (parameter, user logged or not, page present in a cache ...).It is also possible to create your own modules for others types of pages.

Public and session service tables

Each of these registrations may be done in the public service table, or in a session service table, accessible only for one user of the Web site. This allows to create services tailor-made for one user.

Eliom will try to find the page, in that order:

  • in the session service table,
  • in the public service table,
  • the fallback in the session table, if the coservice has expired,
  • the fallback in the public table, if the session has expired.

Session data tables

It is also possible to create a session data table, where you can save information about the session. Each service can look in that table to see if a session is opened or not and get the data.

Example cases

The most commonly used services are:

  • Main services (GET or POST) in public service table for public pages,
  • GET attached coservices in session service table to make the browser's "back" button turn back in the past, and to allow several tabs on different versions of the same page,
  • Actions registered on POST non-attached coservices to make an effect on the server, from any page, and without changing the URL (connection/disconnection for example).

Here is a list of frequent cases and the solution Eliom provides to to solve them. Most of them will be developped in the following.

Display the result of a search (plane ticket, search engines ...)
Use a coservice (with timeout) in the session service table.
Keep information about the session (name of the user ...)
Use a session data table.
A connection or disconnection box on each page of your site
Use actions registered on a non-attached coservices to set or remove data from a session data table.
Add something in a shopping basket
Use an action registered on a non-attached coservice, with the name of the items as parameter. The action saves the shopping basket in a session data table. Thus, the shopping basket will remain even if the user pushes the back button of the browser.
Book a ticket (in several steps)
Each step creates new (GET) coservices (with or without parameters, all attached to the service displaying the main booking page) according to the data entered by the user. These coservices are registered in the session table (with a timeout for the whole session or for each of them). Thus the user can go back to a previous situation, or keep several proposals on differents tabs before choosing one.
...
Help us to complete this list by giving your examples or asking questions about other cases! Thank you!

.

Ocsigen

This page has been generated by Ocsimore. If you are a member of the Ocsigen team, you can log in to modify the pages.