I would like to have a single endpoint to handle arbitrary form submits. Is there a way to get the form-data as a Map<string, string> or equivalent in the sitelet endpoint handle?

  • loic.denuziere

    You can use ctx.Request.Post : ParameterCollection. That being said, it would indeed be useful to be able to use [<FormData>] (or [<Query>] too actually) on a Map<string, _> or Dictionary<string, _> in a sitelet endpoint definition, similarly to how you can use those types for an arbitrary JSON object.

    • Lamk

      Hi Loïc, thanks for the reply. I ended up using MimeKit to decode the body of the request into two separate maps Text values and File values.

      In case it might help someone, here's the decode function with MimeKit:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      
      open MimeKit
          
      type Name = Name of string
      
      type TextPart =
          { Name: Name
            Text: string }
      
      type FilePart =
          { Name: Name
            FileName: string
            ContentType: string 
            Stream: Stream}
      
      let decode (contentType: string) (body: Stream) =
          let load (c: ContentType) (b: Stream) = MimeEntity.Load(c, b)
          let contentType = ContentType.Parse contentType
      
          match load contentType body with
          | :? MimeKit.Multipart as multipart -> 
              multipart
              |> Seq.fold (fun (texts, files) part ->
                  let key = part.ContentDisposition.Parameters.Item "name"
      
                  // Form data can contain multiple times the same key
                  // For example a file input can submit multiple files from the same input
                  match part with
                  | :? MimeKit.TextPart as m -> texts @ [ { Name = Name key; Text = m.Text } ], files 
                  | :? MimeKit.MimePart as m -> texts, files @ [ { Name = Name key; FileName = m.FileName; ContentType = m.ContentType.MimeType; Stream = m.ContentObject.Stream } ]
                  | _ -> (texts, files)) ([], [])
              |> (fun (texts, files) -> texts |> List.groupBy (fun t -> t.Name) |> Map.ofList, files |> List.groupBy (fun f -> f.Name) |> Map.ofList)
              |> Some
      
          | _ -> None