-
Notifications
You must be signed in to change notification settings - Fork 266
Expand file tree
/
Copy pathMiddleware.fs
More file actions
134 lines (110 loc) · 5.94 KB
/
Middleware.fs
File metadata and controls
134 lines (110 loc) · 5.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
[<AutoOpen>]
module Giraffe.Middleware
open System
open System.Runtime.CompilerServices
open System.Threading.Tasks
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.DependencyInjection.Extensions
open Microsoft.IO
// ---------------------------
// Default middleware
// ---------------------------
type GiraffeMiddleware(next: RequestDelegate, handler: HttpHandler, loggerFactory: ILoggerFactory) =
do
if isNull next then
raise (ArgumentNullException("next"))
let logger = loggerFactory.CreateLogger<GiraffeMiddleware>()
// pre-compile the handler pipeline
let func: HttpFunc = handler earlyReturn
member __.Invoke(ctx: HttpContext) =
task {
let start = System.Diagnostics.Stopwatch.GetTimestamp()
let! result = func ctx
if logger.IsEnabled LogLevel.Debug then
let freq = double System.Diagnostics.Stopwatch.Frequency
let stop = System.Diagnostics.Stopwatch.GetTimestamp()
let elapsedMs = (double (stop - start)) * 1000.0 / freq
logger.LogDebug(
"Giraffe returned {SomeNoneResult} for {HttpProtocol} {HttpMethod} at {Path} in {ElapsedMs}",
(if result.IsSome then "Some" else "None"),
ctx.Request.Protocol,
ctx.Request.Method,
ctx.Request.Path.ToString(),
elapsedMs
)
if (result.IsNone) then
return! next.Invoke ctx
}
// ---------------------------
// Error Handling middleware
// ---------------------------
type GiraffeErrorHandlerMiddleware(next: RequestDelegate, errorHandler: ErrorHandler, loggerFactory: ILoggerFactory) =
do
if isNull next then
raise (ArgumentNullException("next"))
member __.Invoke(ctx: HttpContext) =
task {
try
return! next.Invoke ctx
with ex ->
let logger = loggerFactory.CreateLogger<GiraffeErrorHandlerMiddleware>()
try
let func = (Some >> Task.FromResult)
let! _ = errorHandler ex logger func ctx
return ()
with ex2 ->
logger.LogError(EventId(0), ex, "An unhandled exception has occurred while executing the request.")
logger.LogError(
EventId(0),
ex2,
"An exception was thrown attempting to handle the original exception."
)
}
// ---------------------------
// Extension methods for convenience
// ---------------------------
[<Extension>]
type ApplicationBuilderExtensions() =
/// <summary>
/// Adds the <see cref="GiraffeMiddleware" /> into the ASP.NET Core pipeline. Any web request which doesn't get handled by a surrounding middleware can be picked up by the Giraffe <see cref="HttpHandler" /> pipeline.
///
/// It is generally recommended to add the <see cref="GiraffeMiddleware" /> after the error handling, static file and any authentication middleware.
/// </summary>
/// <param name="builder">The ASP.NET Core application builder.</param>
/// <param name="handler">The Giraffe <see cref="HttpHandler" /> pipeline. The handler can be anything from a single handler to an entire web application which has been composed from many smaller handlers.</param>
/// <returns><see cref="Microsoft.FSharp.Core.Unit"/></returns>
[<Extension>]
static member UseGiraffe(builder: IApplicationBuilder, handler: HttpHandler) =
builder.UseMiddleware<GiraffeMiddleware> handler |> ignore
/// <summary>
/// Adds the <see cref="GiraffeErrorHandlerMiddleware" /> into the ASP.NET Core pipeline. The <see cref="GiraffeErrorHandlerMiddleware" /> has been configured in such a way that it only invokes the <see cref="ErrorHandler" /> when an unhandled exception bubbles up to the middleware. It therefore is recommended to add the <see cref="GiraffeErrorHandlerMiddleware" /> as the very first middleware above everything else.
/// </summary>
/// <param name="builder">The ASP.NET Core application builder.</param>
/// <param name="handler">The Giraffe <see cref="ErrorHandler" /> pipeline. The handler can be anything from a single handler to a bigger error application which has been composed from many smaller handlers.</param>
/// <returns>Returns an <see cref="Microsoft.AspNetCore.Builder.IApplicationBuilder"/> builder object.</returns>
[<Extension>]
static member UseGiraffeErrorHandler(builder: IApplicationBuilder, handler: ErrorHandler) =
builder.UseMiddleware<GiraffeErrorHandlerMiddleware> handler
[<Extension>]
type ServiceCollectionExtensions() =
/// <summary>
/// Adds default Giraffe services to the ASP.NET Core service container.
///
/// The default services include features like <see cref="Json.ISerializer"/>, <see cref="Xml.ISerializer"/>, <see cref="INegotiationConfig"/> or more. Please check the official Giraffe documentation for an up to date list of configurable services.
/// </summary>
/// <returns>Returns an <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection"/> builder object.</returns>
[<Extension>]
static member AddGiraffe(svc: IServiceCollection) =
svc.TryAddSingleton<RecyclableMemoryStreamManager>(fun _ -> RecyclableMemoryStreamManager())
svc.TryAddSingleton<Json.ISerializer>(fun _ ->
Json.Serializer(Json.Serializer.DefaultOptions) :> Json.ISerializer
)
svc.TryAddSingleton<Xml.ISerializer>(fun sp ->
SystemXml.Serializer(SystemXml.Serializer.DefaultSettings, sp.GetService<RecyclableMemoryStreamManager>())
:> Xml.ISerializer
)
svc.TryAddSingleton<INegotiationConfig, DefaultNegotiationConfig>()
svc