Web API
in a F#-way
@nakamura_toIntroduction
twitter
works
F# and ASP.NET Web API
F# is powerful.
ASP .NET Web API is nice.
But combination of them is not so good.
- mutable objects
- null
- customization by attribute and inheritance
Enku
Enku - F# Lightweight Web API Framework
- built on top of ASP.NET Web API
- functions instead of attributes
- composition instead of inheritance
- options instead of nulls
- async computation expression insteadof Task
- validation
Enku Example
http://hostname/example
route "example" <| fun _ ->
[
get, fun req -> async {
return Response.Ok "hello" }
post, fun req -> async {
let! content = Request.asyncReadAsString req
return Response.Ok content }
any, fun req -> async {
return Response.NotFound "not found" }
],
fun req e -> Response.InternalServerError e
Request and Response
http://hostname/example/123?name=hoge&age=30
route "example/{?id}" <| fun _ ->
[
get, fun req -> async {
let id: string option = Request.getRouteValue "id" req
let routeValues: Map<string, string> = Request.getRouteValueMap req
let name: string option = Request.getQueryString "name" req
let queryStrings: Map<string, string> = Request.getQueryStringMap req
return Response.BadRequest "" }
post, fun req -> async {
let! (content1: string) = Request.asyncReadAsString req
let! (content2: Stream) = Request.asyncReadAsStream req
// Content-Type: application/x-www-form-urlencoded
let! (content3: Map<string, string list>) = Request.asyncReadAsForm req
return Response.``200`` "" }
],
fun req e -> Response.InternalServerError e
Request Header and
Response Header
route "example" <| fun _ ->
[
get, fun req -> async {
let host = RequestHeader.Host req
if Option.isSome host then
return Response.Ok host.Value
else
return Response.Redirect ""
|> Response.appendHeaders
[ ResponseHeader.Location <| Uri "http://www.google.co.jp/"]}
],
fun req e -> Response.InternalServerError e
Constraint Composition
let exists value req = Option.isSome value
route "example/{?id}" <| fun req ->
let id = Request.getRouteValue "id" req
[
get <&> exists id, fun req -> async {
return Response.Ok "hello" }
put <|> post, fun req -> async {
let! content = Request.asyncReadAsString req
return Response.Ok content }
],
fun req e -> Response.InternalServerError e
Interceptor
let log = Around(fun req action -> async {
printfn "BEFORE"
try
return! action req
finally
printfn "AFTER" })
route "example" <| fun _ ->
Advice.around [log] <|
[
get, fun req -> async {
printfn "INVOKED"
return Response.Ok "hello" }
],
fun req e -> Response.InternalServerError e
output
BEFORE
INVOKED
AFTER
Validation
module V = Validator
route "example" <| fun _ ->
[
get, fun req -> async {
let qs: Map<string, string> = Request.getQueryStringMap req
let vc = ValidationContext()
let name:string option = vc.Eval(qs,"name",V.required)
let age:int option = vc.Eval(qs,"age",V.int <+> V.range 0 21 <+> V.required)
match vc.Errors with
| [] -> ()
| h :: _ -> Response.BadRequest h |> Routing.exit
return Response.Ok "" }
],
fun req e -> Response.InternalServerError e
Summary
Enku encourages you to build Web API in a F#-way.
It's easy to customize Enku's default behaviors, because function compositions are available.
Thank you.
Q and A
web api in a fsharp way
By nakamura_to
web api in a fsharp way
- 3,550