Warning: Reason support is experimental. We are looking for beta-tester and contributors.

Services

Table of contents

Introduction

Services are entry points to your Web site. A service is usually attached to a URL, and it generates a Web page. There are other kinds of services, e.g., services that are identified by special GET or POST parameters, and services representing redirections.

A service is composed of:

  • some identification data, allowing Eliom to choose which service should answer an incoming request; and
  • a service handler that will generate the answer.

Services are most commonly used to create links and forms towards a service, using for example the function Eliom_content.Html.D.a. See chapter Creating links and forms for more information.

Manipulation of Eliom services can be done through the values of type type Eliom_service.t (see Eliom_service_sigs.S.t). The service creation can be split in two steps:

  • create a value of type Eliom_service.t, most commonly by using the function Eliom_service.create; and
  • register a service handler, e.g., using Eliom_registration.Html.register. The handler receives the server parameters (GET and POST) and is responsible for producing the content sent to the client.

The rest of this chapter focuses on service creation. See chapter Implementing service handlers for more information on handler implementation and registration.

Service creation

Warning: in this manual, we use the term service both to denote a value of type Eliom_service.t –that only contains some location information about a service–, or a fully registered service, that is also composed of a service handler. In case of ambiguities, we will use service –in green monotype– to designate a value of type Eliom_service.t.

The standard way to create a service is a call of the form Eliom_service.create ~meth ~path (), where

Service method

Services can respond to any of the GET, POST, PUT, and DELETE HTTP methods.

  • The GET method is intended to be used to retrieve a document from the server. The page is generated mainly according to the information contained in the URL. URLs may contain parameters (consisting of name-value pairs in the URL string), and these parameters may come from HTML forms (or not).
  • The POST method is used to send data to the server (files, for example), but also values coming from an HTML form. Data is sent in the body of the HTTP request. It is possible to use the POST method with an empty body. In HTML, it is not possible to mix GET and POST parameters in forms, but it is possible to use a POST form with (fixed) GET parameters in the URL.
  • The PUT and DELETE methods are mostly used to implement RESTful applications.

The corresponding Eliom_service_sigs.TYPES.meth constructors are Get g, Post (g, p), Put g, and Delete g, where g and p belong in Eliom_parameter.params_type and correspond to the GET and POST parameters.

Service parameters

GET services expect only GET parameters, i.e., parameters that appear in the URL. POST, PUT, and DELETE services can also take POST parameters which are part of the body of the request.

Values of the type Eliom_parameter.params_type represent the set of expected arguments with their types. They are built using combinators from the Eliom_parameter module. See chapter Service parameters for a detailled description of this module.

Type information associated to each argument allows Eliom to automatically convert the actual parameters into the corresponding OCaml types. If the parameter cannot be converted, the exception Eliom_common.Eliom_Typing_Error is raised. The handling of such errors may be customized by providing the argument ~error_handler when registering the service.

Services with path ("regular" services)

Services attached to paths, or more simply regular services, are the main entry points of sites. They are identified by the path of the URL and by (GET or POST) parameters. They correspond to classical URLs, and they last forever once registered.

For creating a regular service, the Path p constructor of Eliom_service_sigs.S.path_option is used. The path p is represented in Eliom as a list of strings. For example:

["foo"; "bar"] corresponds to the URL foo/bar.
["dir"; ""] corresponds to the URL dir/ (that is: the default page of the directory dir).
The empty list [] is equivalent to [""].

In many cases, the URL corresponding to a POST service must be accessible even when a request is done without the POST parameters; for instance, when typing the URL in the browser, when reloading, when using bookmarks, etc. If that is the case, the programmer needs to implement a separate GET service and register it on the same URL.

Warning:

  • You cannot create a service on path ["foo"] (URL foo, without slash at the end) and another on path ["foo";"bar"] (URL foo/bar), because foo cannot be both a directory and a file. Additionally, be careful not to use a path as a directory with Eliom, if it is a file for Staticmod (and vice versa).
  • ["foo";"bar"] is not equivalent to ["foo/bar"]. In the latter, the "/" will be encoded in the URL.

Pathless services

Pathless services are not attached to a URL path. Such services are created with the No_path constructor of Eliom_service_sigs.S.path_option.

Pathless services are only identified by a special parameter, no matter what the path and the other parameters in the URL are. They are used to implement some behavior that should not be attached to a particular URL. A link to such a service points to the current URL with just an additional special parameter. This is useful when you want the same link or form on several pages (for example, a login box) but you don't want to go to another URL. Pathless services are often used with actions.

Here is a simple example. Suppose that the function remove removes one piece of data from a database (based on the identifier of the data). If you want to put a link on your page to call this function and redisplay the page, just create an action on a pathless service like this:

let remove_action =
  Eliom_registration.Action.create
    ~meth:
      (Eliom_service.Post
         (Eliom_parameter.unit,
          Eliom_parameter.int "id"))
    ~path:Eliom_service.No_path
    (fun () id -> remove id)

Then wherever you want to add a button to do that action (on data id), create a form like:

let open Eliom_content.Html.D in
Form.post_form
  ~service:remove_action
  (fun id_name -> [
       Form.input ~input_type:`Hidden ~name:id_name ~value:id Form.int;
       Form.button_no_value ~button_type:`Submit [txt "submit"]
     ])

Changing URL when calling a pathless service

By default, the URL of links or forms to pathless services is the current page. If you want to combine the service call with a URL change, it is possible to attach a non-attached service to another service using function Eliom_service.attach.

Example:

let service =
  Eliom_service.attach
    ~fallback:myfirstservice
    ~service:myget_coserv'
    ()
in
a ~service [txt "click"] ()

It works with GET or POST pathless services. The fallback must be a GET service without parameters (but you can preapply it).

Options for pathless services

Timeouts

It is possible to use timeouts with pathless services, using the optional parameter ?timeout of the creation functions. For example if your service is intended to show the results of a search, you probably want it to be available only for a short time.

Warning: forgetting timeouts may cause memory leaks!

Disposability

It is possible to set a limit to the number of uses of a pathless service. Just provide the maximum number of uses with the optional ?max_use parameter while creating the service.

Non-standard services

Some kinds of services do not fit in our canonical way of defining services, Eliom_service.create .

Attached services

Attached services are created using the functions Eliom_service.create_attached_get (GET method) and Eliom_service.create_attached_post (POST method).

Anonymous GET attached services are often created dynamically with respect to previous interaction with the user (e.g. filling forms in multiple steps). They handle correctly the classical Web interactions ("back" button, bookmark, tab, ...): you create a new attached service each time you want to record a precise point in the interaction with the user, to be able to come back there later.

Often, they should be used with a restricted scope (see the section service scopes).

POST attached services can be used to customize the behaviour of a URL. Some of their uses are:

  • For the same purpose as GET attached services, but when you don't want this service to be bookmarkable.
  • For performing side effects before serving a page. For example, say you want a disconnection button that leads to the main page of the site, but with the side effect of disconnecting the user. You will use a (named) POST (attached) service.

If an attached service does not exist anymore (e.g., if its timeout has expired, or if the session to which it belongs was closed), the fallback is called.

The fallback of a GET attached service cannot take parameters. But it is possible to use a pre-applied service as fallback.

Attached services can have a timeout and can be disposable, just like pathless services.

Unregistrable services

Some values of type Eliom_service.t cannot be meaningfully associated to service handlers. Such values are called unregistrable services (as opposed to the standard case of registrable services), and represent, for example, links towards external sites, or registrable services pre-applied to some parameters. This allows use of the Eliom_service API consistently for creating links.

External services

It is possible to define external services, that is, services that belong to an external Web site (on the same server or not). To do this, use the function Eliom_service.extern (see Eliom_service_sigs.S.extern).

For example, the following code defines a link to the OCaml Wikipedia page:

Eliom_content.Html.D.a
  (Eliom_service.extern
     ~prefix:"http://en.wikipedia.org"
     ~path:["wiki";""]
     ~meth:
       (Eliom_service.Get
          Eliom_parameter.(suffix (all_suffix "suff")))
     ())
  [txt "OCaml on Wikipedia"]
  ["OCaml"]

Static files service

Staticmod is an Ocsigen Server extension serving static (non-generated) files (for example, images and stylesheets). It can be used together with Eliom. The predefined service Eliom_service.static_dir can be used to make links to static files. It takes as parameter the path of the file.

For example, the following code will create this link: download image.

let open Eliom_content.Html.F in
a (static_dir ()) [txt "download image"] ["ocsigen10.png"]

It is also possible to send static files using Eliom, with Eliom_registration.File. See Sending files.

Pre-applied services

It is possible to preapply the GET parameters of a service to obtain a service without parameters, or only the POST ones. It is done using Eliom_service.preapply (see Eliom_service_sigs.S.preapply). For example:

let s =
  Eliom_service.create
    ~path:(Eliom_service.Path ["serv"])
    ~meth:(Eliom_service.Get Eliom_parameter.int)
    ()

let preappl = Eliom_service.preapply s 3

It is not possible to register a handler on a preapplied service, but you can use them in links or as fallbacks for attached services.

Reload actions

Eliom_service.reload_action (see Eliom_service_sigs.S.reload_action) is a special non-attached action, with special behavior: it has no parameter at all, not even non-attached parameters. Use it if you want to make a link to the current page without non-attached parameters. It is almost equivalent to a POST pathless service without POST parameters, on which you register an action that does nothing, but instead it is using the GET method, so that you can use it with <a> links, not only with forms. Example:

a ~service:Eliom_service.reload_action [txt "cancel"] ()

There is also Eliom_service.reload_action_https (same, but forces use of HTTPS), Eliom_service.reload_action_hidden, and Eliom_service.reload_action_https_hidden. "Hidden" means that they keep GET non-attached parameters.

Use Eliom_service.reload_action_hidden for example after a POST request, if you want to do a redirection towards the same page without POST parameters (and thus prevent from reposting data if the user reloads the page).

Service identification

Eliom has a sophisticated service identification mechanism to choose the service handler to be executed –given an incoming request– with respect to many criteria:

  • the path of the requested URL,
  • the names of the (GET or POST) parameters,
  • some internal (GET or POST) parameter, added automatically,
  • the HTTP method,
  • the session the client belongs to (or client side process, or session group),
  • ...

But the user does not usually need to bother with this. Eliom abstracts this mechanism away through its three main kinds of services (regular services, pathless services, and attached services), which we have described.

Service scopes

By default, services are accessible to anybody (scope "site"). It is possible to restrict the scope of a service, making it available only to a session, a client side process, or a group of sessions.

To limit the scope of a service, just add the argument ~scope to the Eliom_registration.Html.register function (same for the other register variants). The default scope is Eliom_common.site_scope.

The same service can be registered with several scopes. This makes it possible, for example, to generate custom services for a specific user. Eliom will try to find the service by trying the following (in order):

  • scope "client-side process",
  • scope "session",
  • scope "group of session",
  • and finally scope "site".

Tips

  • All services created during initialization must be registered (with "site" scope) during the initialization phase of your module. If not, the server will not start, and an appropriate error message will appear in the logs. This prevents broken links.
  • Services may be registered only during the server's initialization phase (while reading the site configuration) or while processing a request, because Eliom must know the information about the site. Be very careful about this if you want to use static linking (see the section on static linking in the chapter about Compiling and configuring Eliom modules).
  • All regular services (but not pathless and attached services) need to be created in a module loaded inside a <host> tag of the configuration file. It is not possible to accomplish this using modules loaded inside <extension> or <library>.
  • If you create new main services dynamically, you will dynamically create new URLs! This may be dangerous as they will disappear if you stop the server. Be very careful to re-create these URLs when you relaunch the server, otherwise, some external links or bookmarks will be broken!
    The use of that feature is discouraged for services without timeout, as such services will be available only until the end of the server process (and it is not possible to re-create them with the same key).
  • Do not register the same service in the same scope twice, and do not replace a service by a directory (or vice versa). If this happens during the initialization phase, the server won't start. If this happens after server startup, it will be ignored (with a warning in the logs).
  • GET attached services (without POST parameters) can be registered only with a regular service without GET/POST parameters as fallback. But it may be //preapplied//.
  • The registration of (main) services must be completed before the end of the loading of the module. It is not possible to launch an Lwt thread with the intention that it will register a service later, as registering a service needs access to config file information (for example the directory of the site). If you do this, the server will raise an exception most of the time, but you may also get unexpected results (if the thread is executed while another site is loaded). If you use threads in the initialization phase of your module (for example if you need information from a database), use Lwt_unix.run to wait for the end of the thread.
  • Some services can be registered multiple times, with different options. This allows for example choosing between different handlers when the request is done in a particular session or protocol (HTTP or HTTPS).