In this page we will get an overview of how you can use Fleece together with Giraffe.
A minimal integration can be done by looking at how Giraffe implements the method WriteJsonAsync and function json:
open Giraffe
open System.IO
open System.Text
// task computation builder from TaskBuilder.fs:
open FSharp.Control.Tasks.V2.ContextInsensitive
// Fleece and Json related:
open Fleece.SystemJson
open Fleece.SystemJson.Operators
module BusinessApp=
module Json =
let inline json (dataObj ) : HttpHandler =
fun (_ : HttpFunc) (ctx : HttpContext) ->
ctx.SetContentType "application/json; charset=utf-8"
toJson dataObj // turn dataObj into Json
|> string // get the Json string
|> Encoding.UTF8.GetBytes // turn the string into bytes
|> ctx.WriteBytesAsync // write bytes to the response
let inline bindJsonAsync (ctx : HttpContext) =
task {
use reader = new StreamReader(ctx.Request.Body)
let! body = reader.ReadToEndAsync()
return parseJson body
}
In the web API part of your business app you would then do something like the code below:
open Giraffe
open FSharp.Control.Tasks.V2.ContextInsensitive
// we open the Json helpers we defined last in order to avoid using the default "json" function from Giraffe:
open BusinessApp.Json
type Person = { Name : string }
with
static member JsonObjCodec =
fun name -> { Name = name }
<!> jreq "name" (Some << fun x -> x.Name)
let personHandler =
fun (next : HttpFunc) (ctx : HttpContext) ->
task {
match! bindJsonAsync ctx with // instead of using ctx.BindJsonAsync we use the function above
| Ok (person:Person)->
return! json person next ctx
// and ideally we would deal with case when the parsing fails as well
}
The benefit of doing an integration in this way is:
- You get a compilation error when trying to use types that don't have the proper functions defined.
- You avoid having to use runtime reflection to bind and serialize the Json.
- You have more control over the serialization than with System.Text.Json or Newtonsoft.Json
module Giraffe
namespace System
namespace System.Threading
namespace System.Threading.Tasks
namespace System.IO
Multiple items
type Request =
new : unit -> Request
member Body : Stream
fake definition
--------------------
new : unit -> Request
type Stream =
inherit MarshalByRefObject
interface IAsyncDisposable
interface IDisposable
new : unit -> unit
member BeginRead : buffer: byte [] * offset: int * count: int * callback: AsyncCallback * state: obj -> IAsyncResult
member BeginWrite : buffer: byte [] * offset: int * count: int * callback: AsyncCallback * state: obj -> IAsyncResult
member Close : unit -> unit
member CopyTo : destination: Stream -> unit + 1 overload
member CopyToAsync : destination: Stream -> Task + 3 overloads
member CreateWaitHandle : unit -> WaitHandle
...
<summary>Provides a generic view of a sequence of bytes. This is an abstract class.</summary>
val failwith : message:string -> 'T
<summary>Throw a <see cref="T:System.Exception" /> exception.</summary>
<param name="message">The exception message.</param>
<returns>The result value.</returns>
Multiple items
type HttpContext =
new : unit -> HttpContext
member SetContentType : s:string -> 'a
member WriteBytesAsync : b:byte array -> Task<HttpContext option>
member Request : Request
--------------------
new : unit -> HttpContext
val __ : HttpContext
val s : string
Multiple items
val string : value:'T -> string
<summary>Converts the argument to a string using <c>ToString</c>.</summary>
<remarks>For standard integer and floating point values the and any type that implements <c>IFormattable</c><c>ToString</c> conversion uses <c>CultureInfo.InvariantCulture</c>. </remarks>
<param name="value">The input value.</param>
<returns>The converted string.</returns>
--------------------
type string = System.String
<summary>An abbreviation for the CLI type <see cref="T:System.String" />.</summary>
<category>Basic Types</category>
val b : byte array
Multiple items
val byte : value:'T -> byte (requires member op_Explicit)
<summary>Converts the argument to byte. This is a direct conversion for all
primitive numeric types. For strings, the input is converted using <c>Byte.Parse()</c>
with InvariantCulture settings. Otherwise the operation requires an appropriate
static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted byte</returns>
--------------------
[<Struct>]
type byte = System.Byte
<summary>An abbreviation for the CLI type <see cref="T:System.Byte" />.</summary>
<category>Basic Types</category>
--------------------
type byte<'Measure> =
byte
<summary>The type of 8-bit unsigned integer numbers, annotated with a unit of measure.
The unit of measure is erased in compiled code and when values of this type
are analyzed using reflection. The type is representationally equivalent to
<see cref="T:System.Byte" />.</summary>
<category>Basic Types with Units of Measure</category>
type 'T array = 'T []
<summary>Single dimensional, zero-based arrays, written <c>int[]</c>, <c>string[]</c> etc.</summary>
<remarks>Use the values in the <see cref="T:Microsoft.FSharp.Collections.ArrayModule" /> module to manipulate values
of this type, or the notation <c>arr.[x]</c> to get/set array
values.</remarks>
<category>Basic Types</category>
Multiple items
type Task =
interface IAsyncResult
interface IDisposable
new : action: Action -> unit + 7 overloads
member ConfigureAwait : continueOnCapturedContext: bool -> ConfiguredTaskAwaitable
member ContinueWith : continuationAction: Action<Task,obj> * state: obj -> Task + 19 overloads
member Dispose : unit -> unit + 1 overload
member GetAwaiter : unit -> TaskAwaiter
member RunSynchronously : unit -> unit + 1 overload
member Start : unit -> unit + 1 overload
member Wait : unit -> unit + 4 overloads
...
<summary>Represents an asynchronous operation.</summary>
--------------------
type Task<'TResult> =
inherit Task
new : function: Func<obj,'TResult> * state: obj -> unit + 7 overloads
member ConfigureAwait : continueOnCapturedContext: bool -> ConfiguredTaskAwaitable<'TResult>
member ContinueWith : continuationAction: Action<Task<'TResult>,obj> * state: obj -> Task + 19 overloads
member GetAwaiter : unit -> TaskAwaiter<'TResult>
member Result : 'TResult
static member Factory : TaskFactory<'TResult>
<summary>Represents an asynchronous operation that can return a value.</summary>
<typeparam name="TResult">The type of the result produced by this <see cref="T:System.Threading.Tasks.Task`1" />.</typeparam>
--------------------
Task(action: System.Action) : Task
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken) : Task
Task(action: System.Action, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj) : Task
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken) : Task
Task(action: System.Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task
--------------------
Task(function: System.Func<'TResult>) : Task<'TResult>
Task(function: System.Func<obj,'TResult>, state: obj) : Task<'TResult>
Task(function: System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken) : Task<'TResult>
Task(function: System.Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(function: System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken) : Task<'TResult>
Task(function: System.Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(function: System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(function: System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
type 'T option = Option<'T>
<summary>The type of optional values. When used from other CLI languages the
empty option is the <c>null</c> value. </summary>
<remarks>Use the constructors <c>Some</c> and <c>None</c> to create values of this type.
Use the values in the <c>Option</c> module to manipulate values of this type,
or pattern match against the values directly.
'None' values will appear as the value <c>null</c> to other CLI languages.
Instance methods on this type will appear as static methods to other CLI languages
due to the use of <c>null</c> as a value representation.</remarks>
<category index="3">Options</category>
type HttpFuncResult = Task<HttpContext option>
type HttpFunc = HttpContext -> HttpFuncResult
type HttpHandler = HttpFunc -> HttpFunc
module Giraffe
from Giraffe
namespace System.Text
Multiple items
namespace FSharp
--------------------
namespace Microsoft.FSharp
Multiple items
namespace FSharp.Control
--------------------
namespace Microsoft.FSharp.Control
namespace FSharp.Control.Tasks
module V2
from FSharp.Control.Tasks
module ContextInsensitive
from FSharp.Control.Tasks.V2
namespace Fleece
module SystemJson
from Fleece
module Operators
from Fleece.SystemJson
Multiple items
module Json
from Giraffe.BusinessApp
--------------------
namespace System.Text.Json
val json : dataObj:'a -> HttpFunc -> ctx:HttpContext -> HttpFuncResult (requires member ToJson)
val dataObj : 'a (requires member ToJson)
val ctx : HttpContext
member HttpContext.SetContentType : s:string -> 'a
val toJson : x:'t -> System.Json.JsonValue (requires member ToJson)
<summary>
Maps a value to Json
</summary>
type Encoding =
interface ICloneable
new : unit -> unit + 2 overloads
member Clone : unit -> obj
member Equals : value: obj -> bool
member GetByteCount : chars: nativeptr<char> * count: int -> int + 5 overloads
member GetBytes : chars: nativeptr<char> * charCount: int * bytes: nativeptr<byte> * byteCount: int -> int + 7 overloads
member GetCharCount : bytes: nativeptr<byte> * count: int -> int + 3 overloads
member GetChars : bytes: nativeptr<byte> * byteCount: int * chars: nativeptr<char> * charCount: int -> int + 4 overloads
member GetDecoder : unit -> Decoder
member GetEncoder : unit -> Encoder
...
<summary>Represents a character encoding.</summary>
property Encoding.UTF8: Encoding with get
<summary>Gets an encoding for the UTF-8 format.</summary>
<returns>An encoding for the UTF-8 format.</returns>
(extension) Encoding.GetBytes(chars: inref<System.Buffers.ReadOnlySequence<char>>) : byte []
(+0 other overloads)
Encoding.GetBytes(s: string) : byte []
(+0 other overloads)
Encoding.GetBytes(chars: char []) : byte []
(+0 other overloads)
(extension) Encoding.GetBytes(chars: inref<System.Buffers.ReadOnlySequence<char>>, writer: System.Buffers.IBufferWriter<byte>) : int64
(+0 other overloads)
(extension) Encoding.GetBytes(chars: inref<System.Buffers.ReadOnlySequence<char>>, bytes: System.Span<byte>) : int
(+0 other overloads)
(extension) Encoding.GetBytes(chars: System.ReadOnlySpan<char>, writer: System.Buffers.IBufferWriter<byte>) : int64
(+0 other overloads)
Encoding.GetBytes(chars: System.ReadOnlySpan<char>, bytes: System.Span<byte>) : int
(+0 other overloads)
Encoding.GetBytes(s: string, index: int, count: int) : byte []
(+0 other overloads)
Encoding.GetBytes(chars: char [], index: int, count: int) : byte []
(+0 other overloads)
Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
(+0 other overloads)
member HttpContext.WriteBytesAsync : b:byte array -> System.Threading.Tasks.Task<HttpContext option>
val bindJsonAsync : ctx:HttpContext -> System.Threading.Tasks.Task<'a ParseResult> (requires member OfJson)
val task : FSharp.Control.Tasks.TaskBuilder.TaskBuilderV2
val reader : StreamReader
Multiple items
type StreamReader =
inherit TextReader
new : stream: Stream -> unit + 10 overloads
member Close : unit -> unit
member DiscardBufferedData : unit -> unit
member Dispose : disposing: bool -> unit
member Peek : unit -> int
member Read : unit -> int + 2 overloads
member ReadAsync : buffer: char [] * index: int * count: int -> Task<int> + 1 overload
member ReadBlock : buffer: char [] * index: int * count: int -> int + 1 overload
member ReadBlockAsync : buffer: char [] * index: int * count: int -> Task<int> + 1 overload
...
<summary>Implements a <see cref="T:System.IO.TextReader" /> that reads characters from a byte stream in a particular encoding.</summary>
--------------------
StreamReader(stream: Stream) : StreamReader
(+0 other overloads)
StreamReader(path: string) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, encoding: Encoding) : StreamReader
(+0 other overloads)
StreamReader(path: string, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(path: string, encoding: Encoding) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, encoding: Encoding, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(path: string, encoding: Encoding, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, encoding: Encoding, detectEncodingFromByteOrderMarks: bool, bufferSize: int) : StreamReader
(+0 other overloads)
StreamReader(path: string, encoding: Encoding, detectEncodingFromByteOrderMarks: bool, bufferSize: int) : StreamReader
(+0 other overloads)
property HttpContext.Request: Request with get
property Request.Body: Stream with get
val body : string
StreamReader.ReadToEndAsync() : System.Threading.Tasks.Task<string>
val parseJson : x:string -> 'a ParseResult (requires member OfJson)
<summary>
Parses a Json Text and maps to a type
</summary>
module BusinessApp
from Giraffe
module Json
from Giraffe.BusinessApp
Person.Name: string
val name : string
val jreq : name:string -> getter:('T -> 'param option) -> ConcreteCodec<System.Collections.Generic.KeyValuePair<string,System.Json.JsonValue> list,System.Collections.Generic.KeyValuePair<string,System.Json.JsonValue> list,'param,'T> (requires member OfJson and member ToJson)
<summary>
Derives a concrete field codec for a required field
</summary>
union case Option.Some: Value: 'T -> Option<'T>
<summary>The representation of "Value of type 'T"</summary>
<param name="Value">The input value.</param>
<returns>An option representing the value.</returns>
val x : Person
val personHandler : next:HttpFunc -> ctx:HttpContext -> System.Threading.Tasks.Task<HttpContext option>
val next : HttpFunc
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
<summary>
Represents an OK or a Successful result. The code succeeded with a value of 'T.
</summary>
val person : Person
type Person =
{ Name: string }
static member JsonObjCodec : ConcreteCodec<KeyValuePair<string,JsonValue> list,KeyValuePair<string,JsonValue> list,Person,Person>