From e0b1cf1469827169df8838870eb7d21cd9b93f69 Mon Sep 17 00:00:00 2001 From: Vincent Balat Date: Fri, 29 Dec 2023 16:59:30 +0100 Subject: [PATCH] Doc: Replace basicwebsite tuto by full page on web site programming --- tutos/7.1/manual/basics-server.wiki | 631 ++++++++++++++++++++++++++++ tutos/7.1/manual/basics.wiki | 34 +- tutos/7.1/manual/basicwebsite.wiki | 178 -------- tutos/7.1/manual/intro.wiki | 4 +- tutos/7.1/manual/menu.wiki | 4 +- tutos/dev/manual/basics-server.wiki | 631 ++++++++++++++++++++++++++++ tutos/dev/manual/basics.wiki | 34 +- tutos/dev/manual/basicwebsite.wiki | 178 -------- tutos/dev/manual/intro.wiki | 4 +- tutos/dev/manual/menu.wiki | 4 +- 10 files changed, 1286 insertions(+), 416 deletions(-) create mode 100644 tutos/7.1/manual/basics-server.wiki delete mode 100644 tutos/7.1/manual/basicwebsite.wiki create mode 100644 tutos/dev/manual/basics-server.wiki delete mode 100644 tutos/dev/manual/basicwebsite.wiki diff --git a/tutos/7.1/manual/basics-server.wiki b/tutos/7.1/manual/basics-server.wiki new file mode 100644 index 00000000..c805e626 --- /dev/null +++ b/tutos/7.1/manual/basics-server.wiki @@ -0,0 +1,631 @@ +=Server-side Web sites in one page= + +While Eliom is well known for its unique client-server programming +model, it is also perfectly suited to programming more traditional +websites. This page describes how you can generate Web pages in OCaml, +and handle links, forms, page parameters, sessions, etc. You will see +that you can get very quickly a working Web site without having to learn +innovative concepts and this might be enough for your needs. + +You will then learn how Eliom is simplifying the programming of very +common behaviours by introducing innovative concepts like scoped +sessions or continuation-based Web programming. + +Programming with Eliom will make your website ready for future +evolutions by allowing you to introduce progressively client-side +features like event handlers, fully in OCaml. You will even be able to +turn your website into a [[basics|distributed client-server Web app]], +and even a mobile app if needed in the future, without having to rewrite +anything. + +<> >> + +<
> + +This programming guide assumes you know the //OCaml// language. +Many resources and books are available online. + +>> + +<
> + +//Lwt// is a concurrent programming library for OCaml, initially written +by Jérôme Vouillon in 2001 for the +[[https://github.com/bcpierce00/unison|Unison]] file synchronizer. +It provides an alternative to the more usual preemptive threads approach +for programming concurrent applications, that avoids most problems of concurrent +data access and deadlocks. +It is used by Ocsigen Server and Eliom and has now become one of the +standard ways to implement concurrent applications in OCaml. +All your Web sites must be written in Lwt-compatible way! + +<
e)}}} +and makes it very natural to sequentialize computations without blocking the rest +of the program. +>> + +To learn Lwt, read this [[lwt|short tutorial]], or its [[wiki("lwt"):|user manual]]. + +>> + +<
> +The following code shows how to create a service that answers +for requests at URL {{{http://.../aaa/bbb}}}, by invoking an +Ocaml function {{{f}}} of type: + +< unit -> string Lwt.t + +>> + +Function {{{f}}} generates HTML as a string, taking as first argument the list +of URL parameters (GET parameters), and as second argument the list of POST +parameters (here none). + +<Hello worldWelcome" + +let myservice = + Eliom_service.create + ~path:(Eliom_service.Path ["aaa"; "bbb"]) + ~meth:(Eliom_service.Get Eliom_parameter.any) + () + +let () = + Eliom_registration.Html_text.register + ~service:myservice + f +>> + +{{{Eliom_service.Get Eliom_parameter.any}}} means that the service uses the GET HTTP method +and takes any GET parameter. + +Module {{{Eliom_registration.Html_text}}} is used to register a service sending +HTML as strings. But we recommend to used typed-HTML instead (see below). + +>> + +<
> + +Eliom provides an helper program called {{{eliom-distillery}}} to create your projects easily, with the right dune configuration and all default directories you usually need for a Web application (static files, server logs, database, etc.). We will show how to use eliom-distillery in another section below. + +In this section, we will show how to compile and run a //server-side only// Web site by creating your project manually. + +{{{ +opam install eliom +dune init proj --kind=lib mysite +cd mysite +}}} + +Add {{{(libraries eliom.server)}}} into file {{{lib/dune}}}. + +Create your {{{.ml}}} files in directory {{{lib}}}. +For example, copy the definition and registration of service {{{myservice}}} above. + +Compile: +{{{ +dune build +}}} + +Create a configuration file {{{mysite.conf}}} with this content on your project root directory: +{{{ + + + 8080 + + local/var/log/mysite + local/var/data/mysite + utf-8 + + local/var/run/mysite-cmd + + + + + + + + + + + + + +}}} +Create the missing directories: +{{{ +mkdir -p local/var/log/mysite +mkdir -p local/var/data/mysite +mkdir -p local/var/run +}}} +Launch the application: +{{{ +ocsigenserver -c mysite.conf +}}} +Open URL {{{http://localhost:8080/aaa/bbb}}} with your browser. +>> + +<
> + +TyXML statically checks that your OCaml functions will never +generate wrong HTML. For example a program that could generate a paragraph +containing another paragraph will be rejected at compile time. + +Example of use: +<> + +<<|From Eliom > 2.2 +(you can also use < > to ease the creation of the {{{head}}} tag.) + +(Eliom_tools.F.head + ~title:"Hello" + ~css:[["css";"style.css"]] + ~js:[["libjs";"mylib.js"]; + ["libjs";"otherlib.js"]] + ()) +>> + + +<
> + +===Example of typing error=== + +< Html_types.p ] as 'a) Eliom_content.Html.F.elt = + 'a Eliom_content.Html.elt + but an expression was expected of type + ([< Html_types.p_content_fun ] as 'b) Eliom_content.Html.F.elt = + 'b Eliom_content.Html.elt + Type 'a = [> `P ] is not compatible with type + 'b = + [< `A of Html_types.phrasing_without_interactive + | `Abbr + | `Audio of Html_types.phrasing_without_media + ... + | `Output + | `PCDATA + | `Progress + | `Q + ... + | `Wbr ] + The second variant type does not allow tag(s) `P +>> +Read more about TyXML in this [[html|short tutorial]] or in its [[wiki("tyxml"):|user manual]]. + +>> + +<
> + +To use typed HTML, just replace module {{{Eliom_registration.Html_text}}} +by {{{Eliom_registration.Html}}}: +<> + +===Outputs=== + +Services can return a typed HTML page as in the example above, but also +any other kind of result. To choose the return type, use the register function +from the corresponding submodule of +<>: + +<
>|@@colspan="3"@@Services returning typed HTML pages| +|=@@class="row"@@<>|@@colspan="3"@@Services returning untyped HTML pages as strings| +|=@@class="row"@@<>|@@colspan="3"@@Apply this functor to generate registration functions for services belonging to an Eliom client/server application. These services also return typed HTML pages, but Eliom will automatically add the client-side program as a JS file, and all the data needed (values of all injections, etc.)| +|=@@class="row"@@<>|@@colspan="3"@@Services returning portions of HTML pages.| +|=@@class="row"@@<>|@@colspan="3"@@Services performing actions (server side effects) with or without reloading the page (e.g. login, logout, payment, modification of user information...)| +|=@@class="row"@@<>|@@colspan="3"@@Serve files from the server hard drive| +|=@@class="row"@@<>|@@colspan="3"@@Services returning OCaml values to be sent to a client side OCaml program (this kind of services is used as low level interface for server functions -- see below)| +|=@@class="row"@@<>|@@colspan="3"@@Services returning any OCaml string (array of byte)| +|=@@class="row"@@<>|@@colspan="3"@@Services returning an HTTP redirection to another service| +|=@@class="row"@@<>|@@colspan="3"@@To be used to make the service chose what it sends. Call function {{{send}}} from the corresponding module to choose the output.| +|=@@class="row"@@<>|@@colspan="3"@@Apply this functor to define your own registration module| +>> +>> + +<
> + +Instead of taking GET parameters as an untyped {{{string * string}}} +association list, you can ask Eliom to decode and check parameter types +automatically. + +For example, the following code defines a service at URL {{{/foo}}}, +that will use GET HTTP method, and take one parameter of type {{{string}}}, +named {{{s}}}, and one of type {{{int}}}, named {{{i}}}. +<> + +Then register an OCaml function as handler on this service: + +< + Lwt.return + Eliom_content.Html.F.(html (head (title (txt "")) []) + (body [h1 [txt (s^string_of_int i)]]))) +>> + +The handler takes as first parameter the GET page parameters, typed according to +the parameter specification given while creating the service. +The second parameter is for POST parameters (see below). + +Recompile you program, and to to URL {{{http://localhost:8080/foo?s=hello&i=22}}} +to see the result. + +===Parameters=== + +Module <> +is used to describe the type of service parameters. + +Examples: +<> + +>> + +<
> + +To define a service with POST parameters, just change the {{{~meth}}} parameter. +For example the following example takes the same GET parameters as the service +above, plus one POST parameter of type string, named "mypostparam". +<> + +>> + +<
> + +===Pathless services=== + +Pathless services are not identified by the path in the URL, +but by a name given as parameter. This name can be specified manually +using the {{{~name}}} optional parameter, otherwise, a random name is +generated automatically. +This is used to implement server functions (see below). +If you are programming a client-server Eliom app, you will probably prefer server functions. +If you are using traditional service based Web programming, +use this to make a functionality available from all pages +(for example: log-in or log-out actions, add something in a shopping basket ...). + +<> + +More information +<>. + +===External services=== +Use <> to create +links or forms towards external Web sites as if they were Eliom services. + +===Predefined services=== +Use service <> +to create links towards static files (see example below for images). + +Use service <> and its variants to create links or forms towards the current URL (reload the page). From a client section, you can also call <> to reload the page and restart the client-side program. + +<>, [[interaction|a tutorial about traditional service based Web programming]], API documentation of modules <> and <>. + +This example shows how to insert an image using {{{static_dir}}}: +<> + +>> + + +<
> + +Function <> creates typed links to services with their parameters. +For example, if {{{home_service}}} expects no parameter +and {{{other_service}}} expects a string and an optional int: +<> + +Module <> +defines the form's elements with the usual typed interface from TyXML. +Use this for example if you have a client side program and +want to manipulate the form's content from client side functions +(for example do a server function call with the form's elements' content). + +In contrast, +module <> defines a typed interface +for form elements. Use this for links (see above), or if you program traditional +server-side Web interaction (with or without client-side program). This will statically check that your forms +match the services. Example: + +< + [fieldset + [label ~a:[a_for name] [txt "Name: "]; + Form.input ~input_type:`Text ~name:name Form.int; + br (); + Form.input + ~a:[a_placeholder "Password"] + ~input_type:`Password + ~name:password + Form.string; + br (); + Form.input ~input_type:`Submit ~value:"Connect" Form.string + ]]) () + +>> + +As you can see, function +<> +is used to create a form sending parameters using the POST HTTP method +(and similarly, <> for GET method). +It takes the service as first parameter, and a function that will generate the form. +This function takes the names of the GET or POST parameters as arguments. + +Form elements (like inputs) are also built from using the +<> module. +They take the names as parameters, and a last parameter (like {{{Form.int}}} or {{{Form.string}}}) to match the expected type. + +>> + + +<
> + +Session data is saved on server side in //Eliom references//. + +The following Eliom reference will count the number of visits of a user on a page: + +<> + +And somewhere in your service handler, increment the counter: +<> + +With function <>, +you can create Eliom references without initial value. The initial value is computed for the session +the first time you use it. + +An Eliom reference can be persistant (value saved on hard drive) or volatile (in memory). + +===Scopes=== +Sessions are relative to a browser, and implemented using browser cookies. +But Eliom allows to create Eliom references with other //scopes// than session: + +|<>|Global value for all the Web server| +|<>|Global value for the Eliom app in that subsite of the Web site| +|<>|Value for a group of sessions. For example Ocsigen Start defines a group of session for each user, making it possible to save server side data for all sessions of a user.| +|<>|The usual session data, based on browser cookies| +|<>|Server side data for a given client-side process (a tab of the browser or a mobile app). This is available only with a client-server Eliom app.| + +Applications based on Ocsigen Start use these scopes for user management. +Session or client process data are discarded when a user logs in or out. +But Ocsigen Start also defines scopes +<> +and +<> +which remain even if a user logs in or out. + +When session group is not set (for example the user is not connected), +you can still use the group session scope: in that case, the group contains only +one session. + +>> + + +<
> + +>> + +<
> + +You can use your favourite database library with Ocsigen. +Ocsigen Start's template uses +[[https://github.com/darioteixeira/pgocaml|PG'OCaml]] +(typed queries for Postgresql using a PPX syntax extension). + +Here is an example, taken from Ocsigen Start's demo: +< + [%pgsql dbh "SELECT lastname FROM ocsigen_start.users"]) +>> + +>> + +<
> + +>> + + +<
> + +[[https://github.com/besport/ocsigen-i18n|Ocsigen i18n]] +is an internationalisation library for your OCaml programs. + +Create a .tsv file with, on each line, a key and the text in several languages: + +{{{ +welcome_message Welcome everybody! Bienvenue à tous ! Benvenuti a tutti ! +}}} + +and Ocsigen i18n will automatically generate functions like this one: +< [txt "Welcome everybody!"] + | Fr -> [txt "Bienvenue à tous !"] + | It -> [txt "Benvenuti a tutti !"] +>> + +Ocsigen i18n also defines a syntax extension to use these functions: +<> + +Ocsigen i18n offers many other features: +* Text can be inserted as a TyXML node (as in the example above) or as a string (ex: {{{[%i18n S.welcome_message]}}}), +* Text can be parametrizable, or contain holes (ex: {{{[%i18n welcome ~capitalize:true ~name:"William"]}}}) +* .tsv file can be split into several modules + +Have a look at the +[[https://github.com/besport/ocsigen-i18n|README file]] +to see the full documentation, +and see examples in +[[https://github.com/ocsigen/ocsigen-start/blob/master/template.distillery/demo_i18n.eliom|Ocsigen Start's template]]. + +>> + +<
> + +//Ocsigen Server// is a full featured Web server. + +It is now based on [[https://github.com/mirage/ocaml-cohttp|Cohttp]]. + +It has a powerful +extension mechanism that makes it easy to plug your own OCaml modules +for generating pages. Many extensions are already written: +;<> +: to serve static files. +;[[wiki("eliom"):|Eliom]] +: to create reliable client/server Web applications + or Web sites in OCaml using advanced high level concepts. +;<> +: allows for more options in the configuration file. +;<> +: restricts access to the sites from the config file (to requests coming from a subnet, containing some headers, etc.). +;<> +: restricts access to the sites from the config file using Basic HTTP Authentication. +;<> +: serves CGI scripts. It may also be used to serve PHP through CGI. +;<> +: used to compress data before sending it to the client. +;<> +: sets redirections towards other Web sites from the configuration file. +;<> +: a reverse proxy for Ocsigen Server. + It allows to ask another server to handle the request. +;<> +: changes incoming requests before sending them to other extensions. +;<> +: rewrites some parts of the output before sending it to the client. +;<> +: allows users to have their own configuration files. +;<> +: facilitates server to client communications. + +Ocsigen Server has a <> file mechanism allowing +complex configurations of sites. + +>> diff --git a/tutos/7.1/manual/basics.wiki b/tutos/7.1/manual/basics.wiki index 8b9d0233..6131061a 100644 --- a/tutos/7.1/manual/basics.wiki +++ b/tutos/7.1/manual/basics.wiki @@ -1,12 +1,12 @@ -=All Ocsigen in one page= +=Client-server applications in one page= This page describes the main concepts you need to master to become fully operational with Ocsigen. Use it as your training plan or as a cheatcheet while programming. -Depending on your needs, you may not need to learn all this. -Ocsigen is very flexible and can be used both for very basic page generation -functions (see [[basicwebsite|this tutorial]]) or very complex -client-server Web apps and their mobile counterparts. +Depending on your needs, you may not need to learn all this. Ocsigen is +very flexible and can be used both for Web site programing (see +[[basics-server|this page]]) or more complex client-server Web apps and +their mobile counterparts. In parallel to the reading of that page, we recommend to generate your first [[wiki("ocsigen-start"):|Ocsigen Start]] app to see @@ -164,6 +164,7 @@ from the corresponding submodule of <
>|@@colspan="3"@@Services returning typed HTML pages| +|=@@class="row"@@<>|@@colspan="3"@@Services returning untyped HTML pages as strings| |=@@class="row"@@<>|@@colspan="3"@@Apply this functor to generate registration functions for services belonging to an Eliom client/server application. These services also return typed HTML pages, but Eliom will automatically add the client-side program as a JS file, and all the data needed (values of all injections, etc.)| |=@@class="row"@@<>|@@colspan="3"@@Services returning portions of HTML pages.| |=@@class="row"@@<>|@@colspan="3"@@Services performing actions (server side effects) with or without reloading the page (e.g. login, logout, payment, modification of user information...)| @@ -350,27 +351,8 @@ modules < - [fieldset - [label ~a:[a_for name] [txt "Name: "]; - Form.input ~input_type:`Text ~name:name Form.int; - br (); - Form.input - ~a:[a_placeholder "Password"] - ~input_type:`Password - ~name:password - Form.string; - br (); - Form.input ~input_type:`Submit ~value:"Connect" Form.string - ]]) () - ->> +match the services. See more information in the +<>. >> <
> - -//The code of this tutorial has been tested against Eliom 6.0.// \\ - -<<|wip|This tutorial is work in progress. Please send us any mistake or comment./>> - -In this tutorial, we will show how to use the Ocsigen framework -(mainly Eliom) to write a lightweight Web site by generating pages -using OCaml functions. The goal is to show that, -even though Eliom makes it possible to write complete client-server -Web and mobile apps, -you can still use Eliom even if you don't need all these features (for example if you don't want HTML type checking or client-side features). Besides, this will allow you to extend your Web site in a full Web application if you need, later on. This tutorial is also a good overview of the basics of Eliom. - - - -==@@id="service"@@ A service generating a page== - -The following code shows how to create a service that answers -for requests at URL {{{http://.../aaa/bbb}}}, by invoking an -Ocaml function {{{f}}} of type: - -< unit -> string Lwt.t - ->> - -Function {{{f}}} generates HTML as a string, taking as argument the list of URL parameters (GET parameters). - -<Hello worldWelcome" - -let main_service = - (* create the service and register it at once *) - Eliom_registration.Html_text.create - ~path:(Eliom_service.Path ["aaa"; "bbb"]) - ~meth:(Eliom_service.Get Eliom_parameter.any) - f - ->> - -{{{Eliom_service.Get Eliom_parameter.any}}} means that the service uses the GET method and takes any GET parameter. - -We recommend to use the program {{{eliom-distillery}}} -to generate a template for your application (a Makefile and a default -configuration file for Ocsigen Server). - - -<> - -Modify file {{{mysite.eliom}}} to include the piece of code above, -instead of the default one. -Then compile and run the server by doing: - -<> - -Your page is now available at URL [[http://localhost:8080/aaa/bbb|{{{http://localhost:8080/aaa/bbb}}}]]. - -If you dont want to use the Makefile provided by eliom-distillery, -just replace {{{mysite.eliom}}} by a file {{{mysite.ml}}}, -compile and run with - -<> - -where mysite.conf is adapted from - {{{local/etc/mysite/mysite-test.conf}}} -by replacing mysite.cma by your cmo. - -==@@id="post"@@ POST service== - -Services using the POST HTTP method can be created and registered using the function -<>. - -<> - -What if the user comes back later without POST parameters, for example -because he put a bookmark on this URL? -This may produce an error, to prevent this from happening we can -create a service without POST parameters using the same URL as the first one, -which will be used whenever the POST parameters are not provided. - -<> - -==@@id="misc"@@ Going further== - -That is probably all you need for a very basic Web site in OCaml. - -Instead of generating HTML in OCaml strings, we highly recommend to use -//typed HTML//. It is very easy to use, once you have learned the basics, -and helps a lot to efficiently write modular and valid HTML. -To do this, use module -<> -instead of -<>. -See this -<> -for more information, a comprehensive documentation -<>, -and a more advanced manual -<>. - -Have a look at Eliom's API documentation to see other kinds of services, -for example <> -to create HTTP redirections. - -Eliom also has a way to typecheck forms and GET or POST parameters. -By giving a description of the parameters your service expects, -Eliom will check their presence automatically, and convert them -for you to OCaml types. -See -<> -and <>. - -Eliom also has other ways to identify services (besides just the PATH -in the URL). For example Eliom can identify a service just by a parameter -(whatever the path is). This is called //non-attached coservices// and -this makes it possible for instance to have the same feature on every page -(for example a connection service). -See -<> -and <>. - -One of the main features of Eliom is the ability to write complete -Web and mobile applications in OCaml. Annotations are used to -separate the code to be executed server-side from the client code. -Client functions are translated into Javascript using -[[wiki("js_of_ocaml"):|Ocsigen Js_of_ocaml]]. -See -<> for -a quick introduction, -or <> for a -more comprehensive one. -You can also have a look at -<>. - -Another interesting feature of Eliom is its session model, that uses a -very simple interface to record session data server-side. -It is even possible to choose -the //scope// of this data: either a browser, or a tab, or even a group -of browsers (belonging for instance to a same user). -See -<> -and the beginning of -<>. - -We suggest to continue your reading by one of these tutorials: - * <> (for the people already familiar with OCaml, Lwt, etc.) - * <> - * <> diff --git a/tutos/7.1/manual/intro.wiki b/tutos/7.1/manual/intro.wiki index c393c54d..25c6a4c9 100644 --- a/tutos/7.1/manual/intro.wiki +++ b/tutos/7.1/manual/intro.wiki @@ -61,8 +61,8 @@ documentation. if you plan to build a client-server Web (and/or mobile) app. It will help you to build your first app very quickly, with many code samples to study. -* [[wiki:manual/basicwebsite|Basic web site]] explains how to write a basic -Web site using OCaml functions +* [[wiki:manual/basics-server|Basic web site]] explains how to write a +website in OCaml, if you don't plan to have client-side features yet * If you are fluent in OCaml and want a quick introduction to client-server Web programming with Eliom, read tutorial diff --git a/tutos/7.1/manual/menu.wiki b/tutos/7.1/manual/menu.wiki index 73e59084..359ad4cc 100644 --- a/tutos/7.1/manual/menu.wiki +++ b/tutos/7.1/manual/menu.wiki @@ -1,10 +1,10 @@ = Programmer's guide ==[[intro|Introduction. Ocsigen: where to start?]] -==[[basics|All Ocsigen in one page]] +==[[basics|Client-server applications in one page]] +==[[basics-server|Server-side Web sites in one page]] = Main tutorials ==[[site:/install|Install Ocsigen]] ==[[start|Your first app in 5 minutes]] -==[[basicwebsite|A basic Web site in OCaml]] ==[[application|Client/server application: Graffiti]] ==[[tutowidgets|Eliom apps basics: writing client-server widgets]] ==[[how-to-register-session-data|Session data: Eliom references]] diff --git a/tutos/dev/manual/basics-server.wiki b/tutos/dev/manual/basics-server.wiki new file mode 100644 index 00000000..c805e626 --- /dev/null +++ b/tutos/dev/manual/basics-server.wiki @@ -0,0 +1,631 @@ +=Server-side Web sites in one page= + +While Eliom is well known for its unique client-server programming +model, it is also perfectly suited to programming more traditional +websites. This page describes how you can generate Web pages in OCaml, +and handle links, forms, page parameters, sessions, etc. You will see +that you can get very quickly a working Web site without having to learn +innovative concepts and this might be enough for your needs. + +You will then learn how Eliom is simplifying the programming of very +common behaviours by introducing innovative concepts like scoped +sessions or continuation-based Web programming. + +Programming with Eliom will make your website ready for future +evolutions by allowing you to introduce progressively client-side +features like event handlers, fully in OCaml. You will even be able to +turn your website into a [[basics|distributed client-server Web app]], +and even a mobile app if needed in the future, without having to rewrite +anything. + +<> >> + +<
> + +This programming guide assumes you know the //OCaml// language. +Many resources and books are available online. + +>> + +<
> + +//Lwt// is a concurrent programming library for OCaml, initially written +by Jérôme Vouillon in 2001 for the +[[https://github.com/bcpierce00/unison|Unison]] file synchronizer. +It provides an alternative to the more usual preemptive threads approach +for programming concurrent applications, that avoids most problems of concurrent +data access and deadlocks. +It is used by Ocsigen Server and Eliom and has now become one of the +standard ways to implement concurrent applications in OCaml. +All your Web sites must be written in Lwt-compatible way! + +<
e)}}} +and makes it very natural to sequentialize computations without blocking the rest +of the program. +>> + +To learn Lwt, read this [[lwt|short tutorial]], or its [[wiki("lwt"):|user manual]]. + +>> + +<
> +The following code shows how to create a service that answers +for requests at URL {{{http://.../aaa/bbb}}}, by invoking an +Ocaml function {{{f}}} of type: + +< unit -> string Lwt.t + +>> + +Function {{{f}}} generates HTML as a string, taking as first argument the list +of URL parameters (GET parameters), and as second argument the list of POST +parameters (here none). + +<Hello worldWelcome" + +let myservice = + Eliom_service.create + ~path:(Eliom_service.Path ["aaa"; "bbb"]) + ~meth:(Eliom_service.Get Eliom_parameter.any) + () + +let () = + Eliom_registration.Html_text.register + ~service:myservice + f +>> + +{{{Eliom_service.Get Eliom_parameter.any}}} means that the service uses the GET HTTP method +and takes any GET parameter. + +Module {{{Eliom_registration.Html_text}}} is used to register a service sending +HTML as strings. But we recommend to used typed-HTML instead (see below). + +>> + +<
> + +Eliom provides an helper program called {{{eliom-distillery}}} to create your projects easily, with the right dune configuration and all default directories you usually need for a Web application (static files, server logs, database, etc.). We will show how to use eliom-distillery in another section below. + +In this section, we will show how to compile and run a //server-side only// Web site by creating your project manually. + +{{{ +opam install eliom +dune init proj --kind=lib mysite +cd mysite +}}} + +Add {{{(libraries eliom.server)}}} into file {{{lib/dune}}}. + +Create your {{{.ml}}} files in directory {{{lib}}}. +For example, copy the definition and registration of service {{{myservice}}} above. + +Compile: +{{{ +dune build +}}} + +Create a configuration file {{{mysite.conf}}} with this content on your project root directory: +{{{ + + + 8080 + + local/var/log/mysite + local/var/data/mysite + utf-8 + + local/var/run/mysite-cmd + + + + + + + + + + + + + +}}} +Create the missing directories: +{{{ +mkdir -p local/var/log/mysite +mkdir -p local/var/data/mysite +mkdir -p local/var/run +}}} +Launch the application: +{{{ +ocsigenserver -c mysite.conf +}}} +Open URL {{{http://localhost:8080/aaa/bbb}}} with your browser. +>> + +<
> + +TyXML statically checks that your OCaml functions will never +generate wrong HTML. For example a program that could generate a paragraph +containing another paragraph will be rejected at compile time. + +Example of use: +<> + +<<|From Eliom > 2.2 +(you can also use < > to ease the creation of the {{{head}}} tag.) + +(Eliom_tools.F.head + ~title:"Hello" + ~css:[["css";"style.css"]] + ~js:[["libjs";"mylib.js"]; + ["libjs";"otherlib.js"]] + ()) +>> + + +<
> + +===Example of typing error=== + +< Html_types.p ] as 'a) Eliom_content.Html.F.elt = + 'a Eliom_content.Html.elt + but an expression was expected of type + ([< Html_types.p_content_fun ] as 'b) Eliom_content.Html.F.elt = + 'b Eliom_content.Html.elt + Type 'a = [> `P ] is not compatible with type + 'b = + [< `A of Html_types.phrasing_without_interactive + | `Abbr + | `Audio of Html_types.phrasing_without_media + ... + | `Output + | `PCDATA + | `Progress + | `Q + ... + | `Wbr ] + The second variant type does not allow tag(s) `P +>> +Read more about TyXML in this [[html|short tutorial]] or in its [[wiki("tyxml"):|user manual]]. + +>> + +<
> + +To use typed HTML, just replace module {{{Eliom_registration.Html_text}}} +by {{{Eliom_registration.Html}}}: +<> + +===Outputs=== + +Services can return a typed HTML page as in the example above, but also +any other kind of result. To choose the return type, use the register function +from the corresponding submodule of +<>: + +<
>|@@colspan="3"@@Services returning typed HTML pages| +|=@@class="row"@@<>|@@colspan="3"@@Services returning untyped HTML pages as strings| +|=@@class="row"@@<>|@@colspan="3"@@Apply this functor to generate registration functions for services belonging to an Eliom client/server application. These services also return typed HTML pages, but Eliom will automatically add the client-side program as a JS file, and all the data needed (values of all injections, etc.)| +|=@@class="row"@@<>|@@colspan="3"@@Services returning portions of HTML pages.| +|=@@class="row"@@<>|@@colspan="3"@@Services performing actions (server side effects) with or without reloading the page (e.g. login, logout, payment, modification of user information...)| +|=@@class="row"@@<>|@@colspan="3"@@Serve files from the server hard drive| +|=@@class="row"@@<>|@@colspan="3"@@Services returning OCaml values to be sent to a client side OCaml program (this kind of services is used as low level interface for server functions -- see below)| +|=@@class="row"@@<>|@@colspan="3"@@Services returning any OCaml string (array of byte)| +|=@@class="row"@@<>|@@colspan="3"@@Services returning an HTTP redirection to another service| +|=@@class="row"@@<>|@@colspan="3"@@To be used to make the service chose what it sends. Call function {{{send}}} from the corresponding module to choose the output.| +|=@@class="row"@@<>|@@colspan="3"@@Apply this functor to define your own registration module| +>> +>> + +<
> + +Instead of taking GET parameters as an untyped {{{string * string}}} +association list, you can ask Eliom to decode and check parameter types +automatically. + +For example, the following code defines a service at URL {{{/foo}}}, +that will use GET HTTP method, and take one parameter of type {{{string}}}, +named {{{s}}}, and one of type {{{int}}}, named {{{i}}}. +<> + +Then register an OCaml function as handler on this service: + +< + Lwt.return + Eliom_content.Html.F.(html (head (title (txt "")) []) + (body [h1 [txt (s^string_of_int i)]]))) +>> + +The handler takes as first parameter the GET page parameters, typed according to +the parameter specification given while creating the service. +The second parameter is for POST parameters (see below). + +Recompile you program, and to to URL {{{http://localhost:8080/foo?s=hello&i=22}}} +to see the result. + +===Parameters=== + +Module <> +is used to describe the type of service parameters. + +Examples: +<> + +>> + +<
> + +To define a service with POST parameters, just change the {{{~meth}}} parameter. +For example the following example takes the same GET parameters as the service +above, plus one POST parameter of type string, named "mypostparam". +<> + +>> + +<
> + +===Pathless services=== + +Pathless services are not identified by the path in the URL, +but by a name given as parameter. This name can be specified manually +using the {{{~name}}} optional parameter, otherwise, a random name is +generated automatically. +This is used to implement server functions (see below). +If you are programming a client-server Eliom app, you will probably prefer server functions. +If you are using traditional service based Web programming, +use this to make a functionality available from all pages +(for example: log-in or log-out actions, add something in a shopping basket ...). + +<> + +More information +<>. + +===External services=== +Use <> to create +links or forms towards external Web sites as if they were Eliom services. + +===Predefined services=== +Use service <> +to create links towards static files (see example below for images). + +Use service <> and its variants to create links or forms towards the current URL (reload the page). From a client section, you can also call <> to reload the page and restart the client-side program. + +<>, [[interaction|a tutorial about traditional service based Web programming]], API documentation of modules <> and <>. + +This example shows how to insert an image using {{{static_dir}}}: +<> + +>> + + +<
> + +Function <> creates typed links to services with their parameters. +For example, if {{{home_service}}} expects no parameter +and {{{other_service}}} expects a string and an optional int: +<> + +Module <> +defines the form's elements with the usual typed interface from TyXML. +Use this for example if you have a client side program and +want to manipulate the form's content from client side functions +(for example do a server function call with the form's elements' content). + +In contrast, +module <> defines a typed interface +for form elements. Use this for links (see above), or if you program traditional +server-side Web interaction (with or without client-side program). This will statically check that your forms +match the services. Example: + +< + [fieldset + [label ~a:[a_for name] [txt "Name: "]; + Form.input ~input_type:`Text ~name:name Form.int; + br (); + Form.input + ~a:[a_placeholder "Password"] + ~input_type:`Password + ~name:password + Form.string; + br (); + Form.input ~input_type:`Submit ~value:"Connect" Form.string + ]]) () + +>> + +As you can see, function +<> +is used to create a form sending parameters using the POST HTTP method +(and similarly, <> for GET method). +It takes the service as first parameter, and a function that will generate the form. +This function takes the names of the GET or POST parameters as arguments. + +Form elements (like inputs) are also built from using the +<> module. +They take the names as parameters, and a last parameter (like {{{Form.int}}} or {{{Form.string}}}) to match the expected type. + +>> + + +<
> + +Session data is saved on server side in //Eliom references//. + +The following Eliom reference will count the number of visits of a user on a page: + +<> + +And somewhere in your service handler, increment the counter: +<> + +With function <>, +you can create Eliom references without initial value. The initial value is computed for the session +the first time you use it. + +An Eliom reference can be persistant (value saved on hard drive) or volatile (in memory). + +===Scopes=== +Sessions are relative to a browser, and implemented using browser cookies. +But Eliom allows to create Eliom references with other //scopes// than session: + +|<>|Global value for all the Web server| +|<>|Global value for the Eliom app in that subsite of the Web site| +|<>|Value for a group of sessions. For example Ocsigen Start defines a group of session for each user, making it possible to save server side data for all sessions of a user.| +|<>|The usual session data, based on browser cookies| +|<>|Server side data for a given client-side process (a tab of the browser or a mobile app). This is available only with a client-server Eliom app.| + +Applications based on Ocsigen Start use these scopes for user management. +Session or client process data are discarded when a user logs in or out. +But Ocsigen Start also defines scopes +<> +and +<> +which remain even if a user logs in or out. + +When session group is not set (for example the user is not connected), +you can still use the group session scope: in that case, the group contains only +one session. + +>> + + +<
> + +>> + +<
> + +You can use your favourite database library with Ocsigen. +Ocsigen Start's template uses +[[https://github.com/darioteixeira/pgocaml|PG'OCaml]] +(typed queries for Postgresql using a PPX syntax extension). + +Here is an example, taken from Ocsigen Start's demo: +< + [%pgsql dbh "SELECT lastname FROM ocsigen_start.users"]) +>> + +>> + +<
> + +>> + + +<
> + +[[https://github.com/besport/ocsigen-i18n|Ocsigen i18n]] +is an internationalisation library for your OCaml programs. + +Create a .tsv file with, on each line, a key and the text in several languages: + +{{{ +welcome_message Welcome everybody! Bienvenue à tous ! Benvenuti a tutti ! +}}} + +and Ocsigen i18n will automatically generate functions like this one: +< [txt "Welcome everybody!"] + | Fr -> [txt "Bienvenue à tous !"] + | It -> [txt "Benvenuti a tutti !"] +>> + +Ocsigen i18n also defines a syntax extension to use these functions: +<> + +Ocsigen i18n offers many other features: +* Text can be inserted as a TyXML node (as in the example above) or as a string (ex: {{{[%i18n S.welcome_message]}}}), +* Text can be parametrizable, or contain holes (ex: {{{[%i18n welcome ~capitalize:true ~name:"William"]}}}) +* .tsv file can be split into several modules + +Have a look at the +[[https://github.com/besport/ocsigen-i18n|README file]] +to see the full documentation, +and see examples in +[[https://github.com/ocsigen/ocsigen-start/blob/master/template.distillery/demo_i18n.eliom|Ocsigen Start's template]]. + +>> + +<
> + +//Ocsigen Server// is a full featured Web server. + +It is now based on [[https://github.com/mirage/ocaml-cohttp|Cohttp]]. + +It has a powerful +extension mechanism that makes it easy to plug your own OCaml modules +for generating pages. Many extensions are already written: +;<> +: to serve static files. +;[[wiki("eliom"):|Eliom]] +: to create reliable client/server Web applications + or Web sites in OCaml using advanced high level concepts. +;<> +: allows for more options in the configuration file. +;<> +: restricts access to the sites from the config file (to requests coming from a subnet, containing some headers, etc.). +;<> +: restricts access to the sites from the config file using Basic HTTP Authentication. +;<> +: serves CGI scripts. It may also be used to serve PHP through CGI. +;<> +: used to compress data before sending it to the client. +;<> +: sets redirections towards other Web sites from the configuration file. +;<> +: a reverse proxy for Ocsigen Server. + It allows to ask another server to handle the request. +;<> +: changes incoming requests before sending them to other extensions. +;<> +: rewrites some parts of the output before sending it to the client. +;<> +: allows users to have their own configuration files. +;<> +: facilitates server to client communications. + +Ocsigen Server has a <> file mechanism allowing +complex configurations of sites. + +>> diff --git a/tutos/dev/manual/basics.wiki b/tutos/dev/manual/basics.wiki index 8b9d0233..6131061a 100644 --- a/tutos/dev/manual/basics.wiki +++ b/tutos/dev/manual/basics.wiki @@ -1,12 +1,12 @@ -=All Ocsigen in one page= +=Client-server applications in one page= This page describes the main concepts you need to master to become fully operational with Ocsigen. Use it as your training plan or as a cheatcheet while programming. -Depending on your needs, you may not need to learn all this. -Ocsigen is very flexible and can be used both for very basic page generation -functions (see [[basicwebsite|this tutorial]]) or very complex -client-server Web apps and their mobile counterparts. +Depending on your needs, you may not need to learn all this. Ocsigen is +very flexible and can be used both for Web site programing (see +[[basics-server|this page]]) or more complex client-server Web apps and +their mobile counterparts. In parallel to the reading of that page, we recommend to generate your first [[wiki("ocsigen-start"):|Ocsigen Start]] app to see @@ -164,6 +164,7 @@ from the corresponding submodule of <
>|@@colspan="3"@@Services returning typed HTML pages| +|=@@class="row"@@<>|@@colspan="3"@@Services returning untyped HTML pages as strings| |=@@class="row"@@<>|@@colspan="3"@@Apply this functor to generate registration functions for services belonging to an Eliom client/server application. These services also return typed HTML pages, but Eliom will automatically add the client-side program as a JS file, and all the data needed (values of all injections, etc.)| |=@@class="row"@@<>|@@colspan="3"@@Services returning portions of HTML pages.| |=@@class="row"@@<>|@@colspan="3"@@Services performing actions (server side effects) with or without reloading the page (e.g. login, logout, payment, modification of user information...)| @@ -350,27 +351,8 @@ modules < - [fieldset - [label ~a:[a_for name] [txt "Name: "]; - Form.input ~input_type:`Text ~name:name Form.int; - br (); - Form.input - ~a:[a_placeholder "Password"] - ~input_type:`Password - ~name:password - Form.string; - br (); - Form.input ~input_type:`Submit ~value:"Connect" Form.string - ]]) () - ->> +match the services. See more information in the +<>. >> <
> - -//The code of this tutorial has been tested against Eliom 6.0.// \\ - -<<|wip|This tutorial is work in progress. Please send us any mistake or comment./>> - -In this tutorial, we will show how to use the Ocsigen framework -(mainly Eliom) to write a lightweight Web site by generating pages -using OCaml functions. The goal is to show that, -even though Eliom makes it possible to write complete client-server -Web and mobile apps, -you can still use Eliom even if you don't need all these features (for example if you don't want HTML type checking or client-side features). Besides, this will allow you to extend your Web site in a full Web application if you need, later on. This tutorial is also a good overview of the basics of Eliom. - - - -==@@id="service"@@ A service generating a page== - -The following code shows how to create a service that answers -for requests at URL {{{http://.../aaa/bbb}}}, by invoking an -Ocaml function {{{f}}} of type: - -< unit -> string Lwt.t - ->> - -Function {{{f}}} generates HTML as a string, taking as argument the list of URL parameters (GET parameters). - -<Hello worldWelcome" - -let main_service = - (* create the service and register it at once *) - Eliom_registration.Html_text.create - ~path:(Eliom_service.Path ["aaa"; "bbb"]) - ~meth:(Eliom_service.Get Eliom_parameter.any) - f - ->> - -{{{Eliom_service.Get Eliom_parameter.any}}} means that the service uses the GET method and takes any GET parameter. - -We recommend to use the program {{{eliom-distillery}}} -to generate a template for your application (a Makefile and a default -configuration file for Ocsigen Server). - - -<> - -Modify file {{{mysite.eliom}}} to include the piece of code above, -instead of the default one. -Then compile and run the server by doing: - -<> - -Your page is now available at URL [[http://localhost:8080/aaa/bbb|{{{http://localhost:8080/aaa/bbb}}}]]. - -If you dont want to use the Makefile provided by eliom-distillery, -just replace {{{mysite.eliom}}} by a file {{{mysite.ml}}}, -compile and run with - -<> - -where mysite.conf is adapted from - {{{local/etc/mysite/mysite-test.conf}}} -by replacing mysite.cma by your cmo. - -==@@id="post"@@ POST service== - -Services using the POST HTTP method can be created and registered using the function -<>. - -<> - -What if the user comes back later without POST parameters, for example -because he put a bookmark on this URL? -This may produce an error, to prevent this from happening we can -create a service without POST parameters using the same URL as the first one, -which will be used whenever the POST parameters are not provided. - -<> - -==@@id="misc"@@ Going further== - -That is probably all you need for a very basic Web site in OCaml. - -Instead of generating HTML in OCaml strings, we highly recommend to use -//typed HTML//. It is very easy to use, once you have learned the basics, -and helps a lot to efficiently write modular and valid HTML. -To do this, use module -<> -instead of -<>. -See this -<> -for more information, a comprehensive documentation -<>, -and a more advanced manual -<>. - -Have a look at Eliom's API documentation to see other kinds of services, -for example <> -to create HTTP redirections. - -Eliom also has a way to typecheck forms and GET or POST parameters. -By giving a description of the parameters your service expects, -Eliom will check their presence automatically, and convert them -for you to OCaml types. -See -<> -and <>. - -Eliom also has other ways to identify services (besides just the PATH -in the URL). For example Eliom can identify a service just by a parameter -(whatever the path is). This is called //non-attached coservices// and -this makes it possible for instance to have the same feature on every page -(for example a connection service). -See -<> -and <>. - -One of the main features of Eliom is the ability to write complete -Web and mobile applications in OCaml. Annotations are used to -separate the code to be executed server-side from the client code. -Client functions are translated into Javascript using -[[wiki("js_of_ocaml"):|Ocsigen Js_of_ocaml]]. -See -<> for -a quick introduction, -or <> for a -more comprehensive one. -You can also have a look at -<>. - -Another interesting feature of Eliom is its session model, that uses a -very simple interface to record session data server-side. -It is even possible to choose -the //scope// of this data: either a browser, or a tab, or even a group -of browsers (belonging for instance to a same user). -See -<> -and the beginning of -<>. - -We suggest to continue your reading by one of these tutorials: - * <> (for the people already familiar with OCaml, Lwt, etc.) - * <> - * <> diff --git a/tutos/dev/manual/intro.wiki b/tutos/dev/manual/intro.wiki index c393c54d..25c6a4c9 100644 --- a/tutos/dev/manual/intro.wiki +++ b/tutos/dev/manual/intro.wiki @@ -61,8 +61,8 @@ documentation. if you plan to build a client-server Web (and/or mobile) app. It will help you to build your first app very quickly, with many code samples to study. -* [[wiki:manual/basicwebsite|Basic web site]] explains how to write a basic -Web site using OCaml functions +* [[wiki:manual/basics-server|Basic web site]] explains how to write a +website in OCaml, if you don't plan to have client-side features yet * If you are fluent in OCaml and want a quick introduction to client-server Web programming with Eliom, read tutorial diff --git a/tutos/dev/manual/menu.wiki b/tutos/dev/manual/menu.wiki index 73e59084..359ad4cc 100644 --- a/tutos/dev/manual/menu.wiki +++ b/tutos/dev/manual/menu.wiki @@ -1,10 +1,10 @@ = Programmer's guide ==[[intro|Introduction. Ocsigen: where to start?]] -==[[basics|All Ocsigen in one page]] +==[[basics|Client-server applications in one page]] +==[[basics-server|Server-side Web sites in one page]] = Main tutorials ==[[site:/install|Install Ocsigen]] ==[[start|Your first app in 5 minutes]] -==[[basicwebsite|A basic Web site in OCaml]] ==[[application|Client/server application: Graffiti]] ==[[tutowidgets|Eliom apps basics: writing client-server widgets]] ==[[how-to-register-session-data|Session data: Eliom references]]