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
istrue
by default but is ignored unlessserveStaticFiles
is also true.
IfserveStaticFiles
isfalse
, then the value forserveIndexFiles
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
istrue
by default but is ignored unlessserveStaticFiles
is also true.
IfserveStaticFiles
isfalse
, then the value forsetStaticDirectory
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 sendAccept-Encoding
headers, or (2) the server doesn't support theAccept-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 (likerodeo
above), it will returnNone
.
Note that thestate
param was combined into a comma delimited string and omitted the emptystate
betweenUT
andWA
. UsegetQueryParams
(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 thatsendText
,sendHtml
, andsendJson
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
, andsendJson
all return tasks so if you aren't using those you may need to end withflush
.