Ocsigen is using the expressiveness of the OCaml language to provide high level semantic concepts dedicated to Web programming. It relieves the programmer of many technical details.
Service based programming
An Ocsigen Web site is written as a single OCaml module. Requests are handled by services, which can be seen as functions called by clicking on an hyperlink or by sending a form.
Services may return HTML pages, but also files, redirections, or many other types of output. There is also a kind of service called action, that just performs a side effect on server side.
The following piece of code defines two services. The first one returns an HTML5 page, the second one returns a redirection to the first one.
let s1 = Html5.register_service ~path:["foo"] ~get_params:unit (fun g p -> ... (* an HTML5 page *)) let s2 = Redirection.register_service ~path:["bar"] ~get_params:unit (fun g p -> return s1 (* redirection to service s1 *))
The register_service function from module Html5 or Redirection takes three parameters:
- The first one, called path, is the URL (here /foo and /bar)
- The second one, called get_params describes the URL paramters expected by the service (unit means no parameter)
- The third one is a function, that will generate the result from GET and POST parameters (here called g and p).
Powerful service identification
Ocsigen provides a very precise way to program traditional Web interaction (URLs, links, forms, bookmarks, sessions ...). The service to be executed is chosen automatically with respect to several criteria, among which:
- The URL
- A service identifier, send as parameter
- The names and types of parameters
- The session the client belongs to
According to the behaviour you want to implement, just choose the kind of identification you want and Ocsigen will do everything for you!
Example: If you want to put a login form on each page of the site, use a non-attached POST coservice, that is, a service that is not linked to an URL, but only to a special (internal) parameter sent in the body of the HTTP request. Thus, calling the service will not change the URL, and you will stay on the same page after connection. This service will perform the action of opening a session.
let login_service = Action.register_post_coservice' ~post_params:(string "login" ** string "password") (fun () (login, password) -> ... (* Check password and open a session *))
The register_post_coservice' allows to define a non-attached POST coservice. As you can see it does not take a path parameter: it is available at any URL. The post_params parameter describes the parameters expected by the service (here two strings, called "login" and "password"). The function that implements the service takes no GET argument, and two POST arguments (here called login and password).
Ocsigen allows to register dynamically new services. This makes possible to create customized services that will depend on previous interaction with one user.
This helps a lot to implement a very common Web interaction scheme: sequences of pages, each one depending on the previous ones. For example if you want to buy a plane ticket, you fill a first form, then the Web site displays a list of tickets. If you choose one of them, the site may for example display another form asking for your personal information, then another for the paiement, etc. On each of these steps, you can duplicate the browser window, or press the back button of the browser, and make another choice. Implementing correctly this behaviour is very tricky with usual Web frameworks and many implementation are erroneous.
To be implemented correctly, such a behaviour requires to save all the information about each step of the interaction with each user. With usual Web programming frameworks, you will probably use tables on server side. But this is tedious work, and many Web sites are bugged for this reason.
With dynamic services, you create a new service for each point of the interaction tree. It is not possible any more to take the wrong one, and this is very easy to write.
This feature is usually called continuation based Web programming.
The following example shows a service with one integer parameter (in the URL, called i). It displays a form, asking for a second integer, that leads to a service that has been registered dynamically. It takes one integer parameter (called j) and displays the result of adding this parameter with i.
let add_service = Html5.register_service ~path:["calc"] ~get_params:(int "i") (fun i () -> let result_service = Html5.register_coservice' ~get_params:(int "j") (fun j () -> ... (* return a page displaying the result of i+j *)) in ... (* return a page with a form asking for a second value, leading to result_service *) )
The value of i is recorded automatically in the memory of the server. In a more complicated example, you could have a longer sequence of services, recording arbitrary data. Each of these services corresponds to one moment of the interaction with the user.
Eliom has some mechanisms to keep memory consumption as low as possible even if you use lots of dynamic services.