Wiz API

Wiz is a collection of functions for interfacing with the native ASP.NET types.

The 4 main parts of Wiz are (1) server configuration, (2) defining routes, (3) reading from incoming requests, and (4) writing an outgoing response. This documentation is grouped by those 4 sections.


Server Configuration

The following functions are for configuring and starting a server

Remember to open Wiz.Server to access these function

genServer

Generates a server configuration

type genServer = unit -> ServerConfig -> ServerConfig

Example

[<EntryPoint>]
let main args =
  genServer()
  |> run

run

Takes a server config and starts a server

type run = ServerConfig -> int

Example

[<EntryPoint>]
let main args =
  genServer()
  |> setHost "0.0.0.0"
  |> setPort 9999
  |> run
// 🔮 Wiz listening on http://0.0.0.0:9999

setHost

Sets the host on the server configuration

type setHost = string -> ServerConfig -> ServerConfig

Example

[<EntryPoint>]
let main args =
  genServer()
  |> setHost "0.0.0.0"
  |> run

setPort

Sets the host on the server configuration

type setPort = string -> ServerConfig -> ServerConfig

Example

[<EntryPoint>]
let main args =
  genServer()
  |> setPort 9999
  |> run

setRoutes

Sets the routes on the server configuration

type setRoutes = Route list -> ServerConfig -> ServerConfig

Example

let helloHandler ctx =
  sendText "hello!" ctx

let byeHandler ctx =
  sendText "bye!" ctx

let myRoutes = [
  get "/hello" helloHandler;
  get "/bye" byeHandler;
]

[<EntryPoint>]
let main args =
  genServer()
  |> setRoutes myRoutes
  |> run

serveStaticFiles

Sets whether to serve static files (default false)

type serveStaticFiles = boolean -> ServerConfig -> ServerConfig

Example

[<EntryPoint>]
let main args =
  genServer()
  |> serveStaticFiles true
  |> run

serveIndexFiles

Sets whether to serve index.html files in a folder by default.

type serveIndexFiles = boolean -> ServerConfig -> ServerConfig

For example, if a folder path has wwwroot/wizard/merlin/index.html then visiting http://example.com/wizard/merlin will automatically serve index.html without needing to visit the full http://example.com/wizard/merlin/index.html.

Example

[<EntryPoint>]
let main args =
  genServer()
  |> serveStaticFiles true // required!
  |> serveIndexFiles false
  |> run
serveIndexFiles is true by default but is ignored unless serveStaticFiles is also true.
If serveStaticFiles is false, then the value for serveIndexFiles is irrelevant.

setStaticDirectory

Sets the directory for serving static content (default wwwroot)

type setStaticDirectory = string -> ServerConfig -> ServerConfig

Example

[<EntryPoint>]
let main args =
  genServer()
  |> serveStaticFiles true // required!
  |> setStaticDirectory "public"
  |> run
setStaticDirectory is true by default but is ignored unless serveStaticFiles is also true.
If serveStaticFiles is false, then the value for setStaticDirectory is irrelevant.

useCompression

Sets whether to compress responses (default true)

type useCompression = boolean -> ServerConfig -> ServerConfig

Example

[<EntryPoint>]
let main args =
  genServer()
  |> serveStaticFiles true
  |> useCompression false
  |> run
If (1) the client doesn't send Accept-Encoding headers, or (2) the server doesn't support the Accept-Encoding methods listed, or (3) the file simply won't benefit from further compression (e.g. JPGs), then ASP.NET won't compress the response. So there's typically little downside to leaving this enabled.

printConfig

Sets whether to compress responses (default true)

type serveIndexFiles = boolean -> ServerConfig -> ServerConfig

Example

[<EntryPoint>]
let main args =
  genServer()
  |> setPort 9999
  |> printConfig
  |> run
printConfig is great for debugging or learning the structure for the server config.

Note on Composability

Since all server configuration is performed on the same config record, it allows for very configurable setups. Here is an example for defining two different config pipelines based on whether in production or development.

Example

let env = Environment.GetEnvironmentVariable("WIZ_ENV")

// if `str` is the current Environment, then run the pipeline
let ifEnv (str: string) pipeline ctx =
  if env = str then
    pipeline ctx
  else
    ctx

let configProd ctx =
  ctx
  |> serveStaticFiles false // we use CDN in prod
  |> setHost "0.0.0.0"
  |> setPort 80

let configDev ctx =
  ctx
  |> serveStaticFiles true
  |> setHost "0.0.0.0"
  |> setPort 9999

[<EntryPoint>]
let main args =
  genServer()
  |> ifEnv "production" configProd
  |> ifEnv "development" configDev
  |> printConfig
  |> run


Routes

The following functions are for defining routes

Remember to open Wiz.Route to access these functions

get

Creates a route that handles GET requests

type get = string -> HttpHandler -> Route

Example

let helloWorldHandler ctx =
  sendText "hello, world!" ctx

let myRoutes = [
  get "/hello" helloWorldHandler
]
//...

post

Creates a route that handles POST requests

type post = string -> HttpHandler -> Route

Example

let myHandler ctx =
  sendText "got post request" ctx

let myRoutes = [
  post "/kapow" myHandler
]
//...

put

Creates a route that handles PUT requests

type put = string -> HttpHandler -> Route

Example

let myHandler ctx =
  sendText "got put request" ctx

let myRoutes = [
  put "/kapow" myHandler
]
//...

delete

Creates a route that handles DELETE requests

type delete = string -> HttpHandler -> Route

Example

let myHandler ctx =
  sendText "got delete request" ctx

let myRoutes = [
  delete "/kapow" myHandler
]
//...

route

Creates a route that handles whatever method you provide

type route = string -> string -> HttpHandler -> Route

Example

let myHandler ctx =
  sendText "got POST request" ctx

let myRoutes = [
  route "POST" "/kapow" myHandler
]
//...

Request

The following functions are for reading information from the incoming request.

Remember to open Wiz.Context to access these function

getHref

Gets the full href of the request

type getHref = HttpContext -> string

Example

// if the user visits: http://localhost:5000/yee/haw?is_cowboy=1
let href = getHref ctx
// "http://localhost:5000/yee/haw?is_cowboy=1"

getOrigin

Gets the request origin (scheme + host)

type getOrigin = HttpContext -> string

Example

// if the user visits: http://localhost:5000/yee/haw?is_cowboy=1
let origin = getOrigin ctx
// "http://localhost:5000"

getScheme

Gets the scheme of the request (e.g. http or https)

type getScheme = HttpContext -> string

Example

let scheme = getScheme ctx
printfn "%s" scheme

getHost

Gets the request Host

type getHost = HttpContext -> string

Example

let host = ctx |> getHost
// e.g. localhost:5000
note that this will include the port if it exists

getHostName

Gets the request Hostname (doesn't include the port)

type getHost = HttpContext -> string

Example

let hostname = ctx |> getHostName
// e.g. localhost

getPort

Gets the request port, if any

type getPort = HttpContext -> int option

Example

match ctx |> getPort with
| Some pt -> printfn "port is: %i" pt
| None -> printfn "no port!"

getMethod

Gets the method of the request

type getMethod = HttpContext -> string

Example

let method = getMethod ctx
printfn "%s" method

getPath

Gets the url path of the request

type getPath = HttpContext -> string

Example

// if href is http://localhost:5000/yee/haw?is_cowboy=1
let path = getPath ctx
// "/yee/haw"

getUrl

Gets the URL resource of the request (path + query string)

type getUrl = HttpContext -> string

Example

// if href is http://localhost:5000/yee/haw?is_cowboy=1
let url = getUrl ctx
// "/yee/haw?is_cowboy=1"

getEncodedUrl

Gets the encoded url resource of the request (path + query string)

type getEncodedUrl = HttpContext -> string

Example

// if href is http://localhost:5000/yee/haw?is_cowboy=1
let url = getUrl ctx
// "%2Fyee%2Fhaw%3Fis_cowboy%3D1"

getQueryString

Gets the query string of the request

type getQueryString = HttpContext -> string

Example

// if href is http://localhost:5000/yee/haw?is_cowboy=1
let qs = getQueryString ctx
// "?is_cowboy=1"

hasQueryParam

Returns whether the query string contains a key

type hasQueryParam = string -> HttpContext -> bool

Example

// if href is http://localhost:5000/yee/haw?is_cowboy=1&rodeo
let myHandler ctx =
  ctx |> hasQueryParam "is_cowboy"
  // true
  ctx |> hasQueryParam "rodeo"
  // true
  ctx |> hasQueryParam "cowgirl"
  // false
Note that rodeo key doesn't have a value.

getQueryParam

Gets a query string value by its key

type getQueryParam = string -> HttpContext -> string option

Example

// given query string of:
//   "?is_cowboy=1&rodeo&state=CA&state=UT&state=&state=WA"
let myHandler ctx =
  ctx |> getQueryParam "is_cowboy"
  // Some "1"
  ctx |> getQueryParam "rodeo"
  // None
  ctx |> getQueryParam "cowgirl"
  // None
  ctx |> getQueryParam "state"
  // Some "CA,UT,WA"
If a key has a blank value (like rodeo above), it will return None.
Note that the state param was combined into a comma delimited string and omitted the empty state between UT and WA. Use getQueryParams (plural) to get an iterable collection.

getQueryParams

Gets a query param value by its key as an iterable string collection (see the StringValues Struct for more info)

type getQueryParams = string -> HttpContext -> StringValues option

Example

// given query string of:
//   "?state=CA&state=UT&state=&state=WA"
match ctx |> getQueryParams "state" with
  | None -> printfn "no state"
  | Some states ->
    for st in states do
      match st with
      | "" -> printfn "state empty"
      | _ -> printfn "state is: %s" st

// state is: CA
// state is: UT
// state empty
// state is: WA
If a key has a blank value, it will return None.

getRouteParam

Gets a route variable by its name

type getRouteParam = string -> HttpContext -> string option

Example

// with a route of /planet/{planet_id}

// if visiting /planet/mars
ctx |> getRouteParam "planet_id"
// Some "mars"

// If you are SURE it won't be empty,
// feel free to unwrap the option, like below.

// if visiting /planet/mercury
ctx |> getRouteParam "planet_id" |> Option.defaultValue ""
// "mercury"

getHeader

Gets a request HTTP header value

type getHeader = string -> HttpContext -> string option

Example

match ctx |> getHeader "User-Agent" with
| Some ct -> printfn $"user agent is: {ct}"
| None -> printfn "no user agent!"

getContentType

Gets the request Content-Type header

type getContentType = HttpContext -> string option

Example

match ctx |> getContentType with
| Some ct -> printfn ct
| None -> printfn "no content type"

getCookie

Gets a cookie value by name

type getCookie = string -> HttpContext -> string option

Example

let myCookie = ctx |> getCookie "my-cookie"
match myCookie with
| Some value -> printfn $"myCookie: {myCookie}"
| None -> printfn $"cookie doesn't exist, bucko"

getBody


Response

The following functions are for writing information to the outgoing response.

Remember to open Wiz.Context to access these function


setStatusCode

Sets the HTTP status code

type setStatusCode = int -> HttpContext -> HttpContext

Example

ctx
|> setStatusCode 403
|> sendText "Access Denied"

setContentType

Sets the Content-Type response header

type setContentType = string -> HttpContext -> HttpContext

Example

ctx
|> setStatusCode 403
|> setContentType "text/plain; charset=UTF-8"
|> send "Access Denied"
Note that sendText, sendHtml, and sendJson will all set the content type automatically.

setHeader

Sets a response header value by a key

type setHeader = string -> string -> HttpContext -> HttpContext

Example

ctx
|> setStatusCode 200
|> setHeader "Server" "Wiz"
|> sendText "Come again soon!"
This will overwrite any values with the same key.

appendHeader

Appends a response header value by a key

type appendHeader = string -> string -> HttpContext -> HttpContext

Example

ctx
|> setStatusCode 200
|> appendHeader "Server" "Wiz"
|> appendHeader "Server" "F#"
|> sendText "Come again soon!"
This will result in duplicate headers if one already exists with the same key.

removeHeader

Remove a response header by a key

type removeHeader = string -> HttpContext -> HttpContext

Example

ctx
|> setStatusCode 200
|> setHeader "Server" "Wiz"
|> appendHeader "Server" "F#"
|> removeHeader "Server" // removes the "Server: Wiz" and "Server: F#" headers
|> sendText "Come again soon!"

setCookie


setCookieMaxAge


setCookiePath


setCookieHttpOnly


setCookieSecure


removeCookie


sendText

Writes text to the body, sets a Content-Type header, and sends a response

type sendText = string -> HttpContext -> Task

Example

ctx
|> setStatusCode 200
|> sendText "Ok!"

sendHtml

Writes HTML to the body, sets a Content-Type header, and sends a response

type sendHtml = string -> HttpContext -> Task

Example

let head = "<head><title>Example</title></head>"
let body = "<body><h1>Welcome to Example</h1></body>"

ctx
|> setStatusCode 200
|> sendHtml $"<!DOCTYPE html><html>{head}{body}</html>"

sendJson


redirect


send


flush

Closes a response if not yet sent

type flush = HttpContext -> Task

Example

// need to end with `flush` since not sending a body
ctx
|> setStatusCode 200
|> flush
All HTTP handlers need to return a task.
sendText, sendHtml, and sendJson all return tasks so if you aren't using those you may need to end with flush.

Context Misc


genCookieOptions