package vif

  1. Overview
  2. Docs

Module Vif.Multipart_formSource

Vif proposes a way to describe a form via types in order to decode a multipart/form-data request and obtain an OCaml record.

Let's take a form such as:

  <form>
    <label for="username">Username:</label>
    <input type="text" name="username" id="username" required />
    <label for="password">Password:</label>
    <input type="password" name="password" id="password" required />
  </form>

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 ]
Sourcetype 'a t

The type for runtime representation of forms of type 'a.

Sourcetype 'a atom

The type for runtime representation of fields of type 'a.

Sourceval string : string atom

string is a representation of the string type.

Sourceval int : int atom

int is a representation of the int type.

Sourcetype ('a, 'b, 'c) orecord

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.

Sourceval record : 'b -> ('a, 'b, 'b) orecord

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.

Sourcetype 'a field

The type for fields belonging to a record of type 'a.

Sourceval field : string -> 'a atom -> 'a field

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.

Sourceval (|+) : ('a, 'b, 'c -> 'd) orecord -> 'c field -> ('a, 'b, 'd) orecord

record |+ field is the open record record augmented with the field field.

Sourceval sealr : ('a, 'b, 'a) orecord -> 'a t

sealr record seals the open record record.

Streaming API of multipart/form-data requests.

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).

Sourcetype part

Type of a part from the multipart/form-data stream.

A part can specify several items of information such as:

  • its name (from the name parameter of the HTML tag)
  • whether this part comes from a user file (with the name of the file)
  • the MIME type of the content (according to the client)
  • as well as the size of the content
Sourceval name : part -> string option

name part is the name of the part (from the name parameter of the HTML tag <input />).

Sourceval filename : part -> string option

filename part is the client's filename of the uploaded file.

Sourceval mime : part -> string option

mime part is the MIME type according to the client's webbrowser.

Sourceval size : part -> int option

size part is the size of the part in bytes.

Sourcetype stream = (part * string Flux.source) Flux.stream

Type of a multipart/form-data stream.

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.