How to use CancellableTask in Giraffe
To use a cancellableTask with Giraffe, we'll need to get the RequestAborted property off the HttpContext.
We'll start off with a slightly longer version but then make a helper for this.
// This just the dance to get Giraffe to compile for this example.
// Really you'd set up these references in an fsproj
#load "../../runtime-scripts/Microsoft.AspNetCore.App-latest-8.fsx"
#r "nuget: Giraffe"
#r "nuget: IcedTasks"
open Microsoft.AspNetCore.Http
open System.Threading
open System.Threading.Tasks
open IcedTasks
open Giraffe
// This is a stand in for some real database call like Npgsql where it would take a CancellationToken
type Database =
static member Get(query, queryParams, cancellationToken: CancellationToken) =
task { do! Task.Delay(10) }
module ExampleVerbose =
// Some function that's doing the real handler's work
let myRealWork next ctx =
cancellableTask {
// use a lamdbda to get the cancellableTask's current CancellationToken
let! result =
fun ct -> Database.Get("SELECT foo FROM bar where baz = @0", [ "@0", "buzz" ], ct)
return! json result next ctx
}
// A helper to get the context's RequestAborted CancellationToken which will give the cancellableTask
// the context to pass long.
let myCustomHandler next (ctx: HttpContext) =
task {
let cancellationToken = ctx.RequestAborted
return! myRealWork next ctx cancellationToken
}
// Some Giraffe App
let app: HttpFunc -> HttpContext -> HttpFuncResult =
route "/"
>=> myCustomHandler
Now that we have the basic outline we can refactor this to make myCustomHandler
take any cancellableTask
module ExampleRefactor1 =
// A helper to get the context's RequestAborted CancellationToken which will give the cancellableTask
// the context to pass long.
let inline cancellableHandler
(cancellableHandler: HttpFunc -> HttpContext -> CancellationToken -> Task<_>)
(next: HttpFunc)
(ctx: HttpContext)
=
task { return! cancellableHandler next ctx ctx.RequestAborted }
// Some function that's doing the real handler's work
let myRealWork next ctx =
cancellableTask {
// use a lamdbda to get the cancellableTask's current CancellationToken
let! result =
fun ct -> Database.Get("SELECT foo FROM bar where baz = @0", [ "@0", "buzz" ], ct)
return! json result next ctx
}
// Some Giraffe App
let app: HttpFunc -> HttpContext -> HttpFuncResult =
route "/"
>=> cancellableHandler myRealWork
namespace Microsoft
namespace Microsoft.AspNetCore
namespace Microsoft.AspNetCore.Http
namespace System
namespace System.Threading
namespace System.Threading.Tasks
namespace IcedTasks
namespace Giraffe
val query: 'a
val queryParams: 'b
val cancellationToken: CancellationToken
Multiple items
[<Struct>] type CancellationToken = new: canceled: bool -> unit member Equals: other: obj -> bool + 1 overload member GetHashCode: unit -> int member Register: callback: Action -> CancellationTokenRegistration + 4 overloads member ThrowIfCancellationRequested: unit -> unit member UnsafeRegister: callback: Action<obj,CancellationToken> * state: obj -> CancellationTokenRegistration + 1 overload static member (<>) : left: CancellationToken * right: CancellationToken -> bool static member (=) : left: CancellationToken * right: CancellationToken -> bool member CanBeCanceled: bool member IsCancellationRequested: bool ...
<summary>Propagates notification that operations should be canceled.</summary>
--------------------
CancellationToken ()
CancellationToken(canceled: bool) : CancellationToken
[<Struct>] type CancellationToken = new: canceled: bool -> unit member Equals: other: obj -> bool + 1 overload member GetHashCode: unit -> int member Register: callback: Action -> CancellationTokenRegistration + 4 overloads member ThrowIfCancellationRequested: unit -> unit member UnsafeRegister: callback: Action<obj,CancellationToken> * state: obj -> CancellationTokenRegistration + 1 overload static member (<>) : left: CancellationToken * right: CancellationToken -> bool static member (=) : left: CancellationToken * right: CancellationToken -> bool member CanBeCanceled: bool member IsCancellationRequested: bool ...
<summary>Propagates notification that operations should be canceled.</summary>
--------------------
CancellationToken ()
CancellationToken(canceled: bool) : CancellationToken
val task: TaskBuilder
Multiple items
type Task = interface IAsyncResult interface IDisposable new: action: Action -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable + 1 overload member ContinueWith: continuationAction: Action<Task,obj> * state: obj -> Task + 19 overloads member Dispose: unit -> unit member GetAwaiter: unit -> TaskAwaiter member RunSynchronously: unit -> unit + 1 overload member Start: unit -> unit + 1 overload member Wait: unit -> unit + 5 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> + 1 overload member ContinueWith: continuationAction: Action<Task<'TResult>,obj> * state: obj -> Task + 19 overloads member GetAwaiter: unit -> TaskAwaiter<'TResult> member WaitAsync: cancellationToken: CancellationToken -> Task<'TResult> + 4 overloads 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: CancellationToken) : Task
Task(action: System.Action, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj) : Task
Task(action: System.Action, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: CancellationToken) : Task
Task(action: System.Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: 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: CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
type Task = interface IAsyncResult interface IDisposable new: action: Action -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable + 1 overload member ContinueWith: continuationAction: Action<Task,obj> * state: obj -> Task + 19 overloads member Dispose: unit -> unit member GetAwaiter: unit -> TaskAwaiter member RunSynchronously: unit -> unit + 1 overload member Start: unit -> unit + 1 overload member Wait: unit -> unit + 5 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> + 1 overload member ContinueWith: continuationAction: Action<Task<'TResult>,obj> * state: obj -> Task + 19 overloads member GetAwaiter: unit -> TaskAwaiter<'TResult> member WaitAsync: cancellationToken: CancellationToken -> Task<'TResult> + 4 overloads 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: CancellationToken) : Task
Task(action: System.Action, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj) : Task
Task(action: System.Action, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: CancellationToken) : Task
Task(action: System.Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: 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: CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task.Delay(delay: System.TimeSpan) : Task
Task.Delay(millisecondsDelay: int) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider) : Task
Task.Delay(delay: System.TimeSpan, cancellationToken: CancellationToken) : Task
Task.Delay(millisecondsDelay: int, cancellationToken: CancellationToken) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider, cancellationToken: CancellationToken) : Task
Task.Delay(millisecondsDelay: int) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider) : Task
Task.Delay(delay: System.TimeSpan, cancellationToken: CancellationToken) : Task
Task.Delay(millisecondsDelay: int, cancellationToken: CancellationToken) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider, cancellationToken: CancellationToken) : Task
val myRealWork: next: HttpFunc -> ctx: HttpContext -> CancellableTask<HttpContext option>
val next: HttpFunc
val ctx: HttpContext
val cancellableTask: CancellableTaskBuilder
<summary> Builds a cancellableTask using computation expression syntax. </summary>
<summary> Builds a cancellableTask using computation expression syntax. </summary>
val result: unit
val ct: CancellationToken
type Database =
static member Get: query: 'a * queryParams: 'b * cancellationToken: CancellationToken -> Task<unit>
static member Database.Get: query: 'a * queryParams: 'b * cancellationToken: CancellationToken -> Task<unit>
val json: dataObj: 'T -> HttpFunc -> ctx: HttpContext -> HttpFuncResult
<summary> Serializes an object to JSON and writes the output to the body of the HTTP response. It also sets the HTTP Content-Type header to application/json and sets the Content-Length header accordingly. The JSON serializer can be configured in the ASP.NET Core startup code by registering a custom class of type <see cref="Json.ISerializer" />. </summary>
<param name="dataObj">The object to be send back to the client.</param>
<param name="ctx"></param>
<typeparam name="'T"></typeparam>
<returns>A Giraffe <see cref="HttpHandler" /> function which can be composed into a bigger web application.</returns>
<summary> Serializes an object to JSON and writes the output to the body of the HTTP response. It also sets the HTTP Content-Type header to application/json and sets the Content-Length header accordingly. The JSON serializer can be configured in the ASP.NET Core startup code by registering a custom class of type <see cref="Json.ISerializer" />. </summary>
<param name="dataObj">The object to be send back to the client.</param>
<param name="ctx"></param>
<typeparam name="'T"></typeparam>
<returns>A Giraffe <see cref="HttpHandler" /> function which can be composed into a bigger web application.</returns>
val myCustomHandler: next: HttpFunc -> ctx: HttpContext -> Task<HttpContext option>
type HttpContext =
override Abort: unit -> unit
member Connection: ConnectionInfo
member Features: IFeatureCollection
member Items: IDictionary<obj,obj>
member Request: HttpRequest
member RequestAborted: CancellationToken
member RequestServices: IServiceProvider
member Response: HttpResponse
member Session: ISession
member TraceIdentifier: string
...
property HttpContext.RequestAborted: CancellationToken with get, set
val app: (HttpFunc -> HttpContext -> HttpFuncResult)
type HttpFunc = HttpContext -> HttpFuncResult
<summary> A HTTP function which takes an <see cref="Microsoft.AspNetCore.Http.HttpContext" /> object and returns a <see cref="HttpFuncResult" />. The function may inspect the incoming <see cref="Microsoft.AspNetCore.Http.HttpRequest" /> and make modifications to the <see cref="Microsoft.AspNetCore.Http.HttpResponse" /> before returning a <see cref="HttpFuncResult" />. The result can be either a <see cref="System.Threading.Tasks.Task" /> of Some HttpContext or a <see cref="System.Threading.Tasks.Task" /> of None. If the result is Some HttpContext then the Giraffe middleware will return the response to the client and end the pipeline. However, if the result is None then the Giraffe middleware will continue the ASP.NET Core pipeline by invoking the next middleware. </summary>
<summary> A HTTP function which takes an <see cref="Microsoft.AspNetCore.Http.HttpContext" /> object and returns a <see cref="HttpFuncResult" />. The function may inspect the incoming <see cref="Microsoft.AspNetCore.Http.HttpRequest" /> and make modifications to the <see cref="Microsoft.AspNetCore.Http.HttpResponse" /> before returning a <see cref="HttpFuncResult" />. The result can be either a <see cref="System.Threading.Tasks.Task" /> of Some HttpContext or a <see cref="System.Threading.Tasks.Task" /> of None. If the result is Some HttpContext then the Giraffe middleware will return the response to the client and end the pipeline. However, if the result is None then the Giraffe middleware will continue the ASP.NET Core pipeline by invoking the next middleware. </summary>
type HttpFuncResult = Task<HttpContext option>
<summary> A type alias for <see cref="System.Threading.Tasks.Task{HttpContext option}" /> which represents the result of a HTTP function (HttpFunc). If the result is Some HttpContext then the Giraffe middleware will return the response to the client and end the pipeline. However, if the result is None then the Giraffe middleware will continue the ASP.NET Core pipeline by invoking the next middleware. </summary>
<summary> A type alias for <see cref="System.Threading.Tasks.Task{HttpContext option}" /> which represents the result of a HTTP function (HttpFunc). If the result is Some HttpContext then the Giraffe middleware will return the response to the client and end the pipeline. However, if the result is None then the Giraffe middleware will continue the ASP.NET Core pipeline by invoking the next middleware. </summary>
val route: path: string -> next: HttpFunc -> ctx: HttpContext -> HttpFuncResult
<summary> Filters an incoming HTTP request based on the request path (case sensitive). </summary>
<param name="path">Request path.</param>
<param name="next"></param>
<param name="ctx"></param>
<returns>A Giraffe <see cref="HttpHandler" /> function which can be composed into a bigger web application.</returns>
<summary> Filters an incoming HTTP request based on the request path (case sensitive). </summary>
<param name="path">Request path.</param>
<param name="next"></param>
<param name="ctx"></param>
<returns>A Giraffe <see cref="HttpHandler" /> function which can be composed into a bigger web application.</returns>
module ExampleRefactor1
from Cancellable-Task-In-Giraffe
val cancellableHandler: cancellableHandler: (HttpFunc -> HttpContext -> CancellationToken -> Task<'a>) -> next: HttpFunc -> ctx: HttpContext -> Task<'a>
val cancellableHandler: (HttpFunc -> HttpContext -> CancellationToken -> Task<'a>)