It is possible to describe this server-side form in this way:
type credential = { username: string; password: string }
let form =
let open Vif.Multipart_form in
let fn username password = { username; password } in
record fn
|+ field "username" string
|+ field "password" string
|> sealr
It is then possible to define a POST route to manage such a form:
let login req _server _cfg =
match Vif.Request.of_multipart_form req with
| Ok { username; password; } -> ...
| Error _ -> ...
let routes =
let open Vif.Uri in
let open Vif.Route in
let open Vif.Type in
[ post (m form) (rel / "login" /?? nil) --> login ]
The type for representing open records of type 'a with a constructor of type 'b. 'c represents the remaining fields to be described using the (|+) operator. An open record initially satisfies 'c = 'b and can be sealed once 'c = 'a.
record fn is an incomplete representation of the record of type 'a with constructor fn. To complete the representation, add fields with (|+) and then seal the record with sealr.
field name t is the representation of the field called name of type t. The name must be unique in relation to the other fields in the form and must also correspond to the name given to the <input /> in the form.
The user may want to manage a request containing a form in the form of a stream. This is particularly useful if you want to upload a file (and, instead of storing it in memory, directly write the received file to a temporary file).
It may be necessary to save part of a form as a file rather than storing it in memory. Vif allows this using its form stream API. The user can observe the parts of a form one after the other and manipulate the content of these parts in the form of a stream:
open Vif
let upload req server _ =
let fn (part, src) =
match Multipart_form.name part with
| Some "file" -> S.Stream.to_file "foo.txt" (S.Stream.from src)
| Some "name" ->
let value = S.(Stream.into Sink.string (Stream.from src)) in
Hashtbl.add form "name" value
| _ -> S.Stream.(drain (from src))
in
let stream = Result.get_ok (Request.of_multipart_form req) in
S.Stream.each fn stream;
match Hashtbl.find_opt form "name" with
| Some value ->
Unix.rename "foo.txt" value;
Response.respond `OK
| _ -> Response.respond `Bad_request
Note: It is important to drain the parts that we are not familiar with.