Request Handling
Falco exposes a uniform API to obtain typed values from IFormCollection
, IQueryCollection
, RouteValueDictionary
, IHeaderCollection
, and IRequestCookieCollection
. All implementations are derivatives of StringCollectionReader
which is an abstraction intended to make it easier to work with the string-based key/value collections.
Route Binding
open Falco
// Assuming a route pattern of /{Name}
let manualRouteHandler : HttpHandler = fun ctx ->
let r = Request.getRoute ctx
let name = r.GetString "Name"
Response.ofPlainText name ctx
let mapRouteHandler : HttpHandler =
Request.mapRoute (fun r ->
r.GetString "Name")
Response.ofPlainText
Query Binding
open Falco
type Person =
{ FirstName : string
LastName : string }
let manualQueryHandler : HttpHandler = fun ctx ->
let q = Request.getQuery ctx
let person =
{ FirstName = q.GetString ("FirstName", "John") // Get value or return default value
LastName = q.GetString ("LastName", "Doe") }
Response.ofJson person ctx
let mapQueryHandler : HttpHandler =
Request.mapQuery (fun q ->
let first = q.GetString ("FirstName", "John") // Get value or return default value
let last = q.GetString ("LastName", "Doe")
{ FirstName = first; LastName = last })
Response.ofJson
Form Binding
The FormCollectionReader
has full access to the IFormFilesCollection
via the _.Files
member.
Note the addition of
Request.mapFormSecure
, which will automatically validate CSRF token for you.
type Person =
{ FirstName : string
LastName : string }
let manualFormHandler : HttpHandler = fun ctx ->
task {
let! f : FormCollectionReader = Request.getForm ctx
let person =
{ FirstName = f.GetString ("FirstName", "John") // Get value or return default value
LastName = f.GetString ("LastName", "Doe") }
return! Response.ofJson person ctx
}
let mapFormHandler : HttpHandler =
Request.mapForm (fun f ->
let first = f.GetString ("FirstName", "John") // Get value or return default value
let last = f.GetString ("LastName", "Doe")
{ FirstName = first; LastName = last })
Response.ofJson
let mapFormSecureHandler : HttpHandler =
Request.mapFormSecure (fun f ->
let first = f.GetString ("FirstName", "John") // Get value or return default value
let last = f.GetString ("LastName", "Doe")
{ FirstName = first; LastName = last })
Response.ofJson
(Response.withStatusCode 400 >> Response.ofEmpty)
multipart/form-data
Binding
Microsoft defines large upload as anything > 64KB, which well... is most uploads. Anything beyond this size and they recommend streaming the multipart data to avoid excess memory consumption.
To make this process a lot easier Falco provides a set of HttpHandler
's analogous to the form handlers above, which utilize an HttpContext
extension method called TryStreamFormAsync()
that will attempt to stream multipart form data, or return an error message indicating the likely problem.
let imageUploadHandler : HttpHandler =
let formBinder (f : FormCollectionReader) : IFormFile option =
f.TryGetFormFile "profile_image"
let uploadImage (profileImage : IFormFile option) : HttpHandler =
// Process the uploaded file ...
// Safely buffer the multipart form submission
Request.mapFormStream formBinder uploadImage
let secureImageUploadHandler : HttpHandler =
let formBinder (f : FormCollectionReader) : IFormFile option =
f.TryGetFormFile "profile_image"
let uploadImage (profileImage : IFormFile option) : HttpHandler =
// Process the uploaded file ...
let handleInvalidCsrf : HttpHandler =
Response.withStatusCode 400 >> Response.ofEmpty
// Safely buffer the multipart form submission
Request.mapFormStreamSecure formBinder uploadImage handleInvalidCsrf
JSON
These handlers use the .NET built-in System.Text.Json.JsonSerializer
.
type Person =
{ FirstName : string
LastName : string }
let jsonHandler : HttpHandler =
{ FirstName = "John"
LastName = "Doe" }
|> Response.ofJson
let mapJsonHandler : HttpHandler =
let handleOk person : HttpHandler =
let message = sprintf "hello %s %s" person.First person.Last
Response.ofPlainText message
Request.mapJson handleOk
let mapJsonOptionsHandler : HttpHandler =
let options = JsonSerializerOptions()
options.DefaultIgnoreCondition <- JsonIgnoreCondition.WhenWritingNull
let handleOk person : HttpHandler =
let message = sprintf "hello %s %s" person.First person.Last
Response.ofPlainText message
Request.mapJsonOption options handleOk