|
15 | 15 |
|
16 | 16 | ---
|
17 | 17 |
|
| 18 | +### Web frameworks |
| 19 | + |
| 20 | +* Standalized way of making web resources available |
| 21 | + * API |
| 22 | + * Content |
| 23 | +* Web frameworks for all language |
| 24 | + * C#, Java, php, ... Python, js, ... |
| 25 | + |
| 26 | +---- |
| 27 | + |
| 28 | +### ASP.NET |
| 29 | + |
| 30 | +* .NET based WebFramework |
| 31 | + * made for C# |
| 32 | +* Supports a wide range of applications |
| 33 | + * Web pages |
| 34 | + * MVC |
| 35 | + * Web API |
| 36 | + * Web Hooks |
| 37 | +* Normally runs in the IIS container |
| 38 | + |
| 39 | +---- |
| 40 | + |
| 41 | +### Giraffe |
| 42 | + |
| 43 | +* (micro) Web framework build as middleware to ASP.NET Core |
| 44 | +* Make use of 'all' build in middlewares |
| 45 | + * static file, authentication, authorization, .. |
| 46 | +* Built as a Functional framework (which we like) |
| 47 | +* Very simple concept |
| 48 | + |
| 49 | +```fsharp |
| 50 | +type HttpFuncResult = Task<HttpContext option> |
| 51 | +type HttpFunc = HttpContext -> HttpFuncResult |
| 52 | +type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult |
| 53 | +``` |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +### `HttpHandler` |
| 58 | + |
| 59 | +* Has a bind method (now called compose) |
| 60 | +* and a operator (>=>) |
| 61 | +* plus some more (as we will see below) |
| 62 | + |
| 63 | +---- |
| 64 | + |
| 65 | +### Prebuilt `HttpHandler`s |
| 66 | + |
| 67 | +* using a prebuilt HttpHandler |
| 68 | + * e.g. `text`, `json`, `setBody`, `GET`, `POST`, `compose` |
| 69 | + |
| 70 | +```fsharp |
| 71 | +let sayHelloWorld : HttpHandler = text "Hello World, from Giraffe" |
| 72 | +``` |
| 73 | + |
| 74 | +---- |
| 75 | + |
| 76 | +### Using `HttpFunc` to create `HttpHandler` |
| 77 | + |
| 78 | +* When you need access to the HttpContext |
| 79 | +```fsharp |
| 80 | +// type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult |
| 81 | +let todo: Endpoint = |
| 82 | + fun next ctx -> |
| 83 | + ServerErrors.NOT_IMPLEMENTED "" next ctx |
| 84 | +``` |
| 85 | +* Applying 'next' and 'ctx' to then subsequent handler. |
| 86 | + |
| 87 | + |
| 88 | +---- |
| 89 | + |
| 90 | +### Explicitly handling async operations |
| 91 | + |
| 92 | +```fsharp |
| 93 | +let sayHelloWorld : HttpHandler = |
| 94 | + fun (next : HttpFunc) (ctx : HttpContext) -> |
| 95 | + task { |
| 96 | + let! person = ctx.BindJsonAsync<Person>() |
| 97 | + let greeting = sprintf "Hello World, from %s" person.Name |
| 98 | + return! text greeting next ctx |
| 99 | + } |
| 100 | +``` |
| 101 | + |
| 102 | +---- |
| 103 | + |
| 104 | +### Bind implementation |
| 105 | + |
| 106 | +```fsharp |
| 107 | +//Note: concept code |
| 108 | +let bind (handler : HttpHandler) (handler2 : HttpHandler) = |
| 109 | + fun (ctx : HttpHandlerContext) -> |
| 110 | + async { |
| 111 | + let! result = handler ctx |
| 112 | + match result with |
| 113 | + | None -> return None |
| 114 | + | Some ctx2 -> |
| 115 | + match ctx2.HttpContext.Response.HasStarted with |
| 116 | + | true -> return Some ctx2 |
| 117 | + | false -> return! handler2 ctx2 |
| 118 | + } |
| 119 | +``` |
| 120 | + |
| 121 | +* Should look much like something we already know from last week. |
| 122 | + |
| 123 | + |
| 124 | +---- |
| 125 | + |
| 126 | +### `Compose` |
| 127 | + |
| 128 | +* Operator (>=>) |
| 129 | +* builds on bind, but is an optimzed version |
| 130 | +* E.g. |
| 131 | + |
| 132 | +```fsharp |
| 133 | +let app = compose (route "/") (Successful.OK "Hello World") |
| 134 | +// or |
| 135 | +let app = route "/" >=> Successful.OK "Hello World" |
| 136 | +``` |
| 137 | + |
| 138 | +---- |
| 139 | + |
| 140 | +### `Choose` |
| 141 | + |
| 142 | +* HttpHandler |
| 143 | +* Iterates throgh multiple HttpHandlers and apply fist that fits |
| 144 | + |
| 145 | +```fsharp |
| 146 | +let app = |
| 147 | + choose [ |
| 148 | + route "/foo" >=> text "Foo" |
| 149 | + route "/bar" >=> text "Bar" |
| 150 | + ] |
| 151 | +``` |
| 152 | + |
| 153 | +---- |
| 154 | + |
| 155 | +### `Warbler` |
| 156 | + |
| 157 | +* Used when you don't want to return static content |
| 158 | +```fsharp |
| 159 | +// ('a -> 'a -> 'b) -> 'a -> 'b |
| 160 | +let warbler f a = f a a |
| 161 | +``` |
| 162 | +* To avoid functions be eagerly loaded |
| 163 | +```fsharp [7] |
| 164 | +// unit -> string |
| 165 | +let time() = System.DateTime.Now.ToString() |
| 166 | +
|
| 167 | +let App = |
| 168 | + choose [ |
| 169 | + route "/normal" >=> text (time()) |
| 170 | + route "/warbler" >=> warbler (fun _ -> text (time())) |
| 171 | + ] |
| 172 | +``` |
| 173 | + |
| 174 | +--- |
| 175 | + |
| 176 | +### Setting up Giraffe |
| 177 | + |
| 178 | +* Install Giraffe nuget package in F# project |
| 179 | + * `PM> Install-Package Giraffe` |
| 180 | + |
| 181 | + |
| 182 | +```fsharp [7-8] |
| 183 | +[<EntryPoint>] |
| 184 | +let main _ = |
| 185 | + Host.CreateDefaultBuilder() |
| 186 | + .ConfigureWebHostDefaults( |
| 187 | + fun webHostBuilder -> |
| 188 | + webHostBuilder |
| 189 | + .Configure(configureApp) |
| 190 | + .ConfigureServices(configureServices) |
| 191 | + |> ignore) |
| 192 | + .Build() |
| 193 | + .Run() |
| 194 | + 0 |
| 195 | +``` |
| 196 | + |
| 197 | +---- |
| 198 | + |
| 199 | +### Defining Giraffe middlewere |
| 200 | + |
| 201 | +```fsharp |
| 202 | +let webApp = ... |
| 203 | +
|
| 204 | +let configureApp (app : IApplicationBuilder) = |
| 205 | + // Add Giraffe to the ASP.NET Core pipeline |
| 206 | + app.UseGiraffe webApp |
| 207 | +
|
| 208 | +let configureServices (services : IServiceCollection) = |
| 209 | + // Add Giraffe dependencies |
| 210 | + services.AddGiraffe() |> ignore |
| 211 | +``` |
| 212 | + |
| 213 | +---- |
| 214 | + |
| 215 | +### Routing |
| 216 | + |
| 217 | +* Is also an `HttpHandler` |
| 218 | +* 'route' being the main |
| 219 | + * `route '/info' >=> info` |
| 220 | +* 'routeCi' - non exact match |
| 221 | + * `route '/info` >=> info` |
| 222 | +* 'routex' - regex |
| 223 | + * `routex '/info(/?)' >=> info` |
| 224 | +* .... |
| 225 | + |
| 226 | +---- |
| 227 | + |
| 228 | +### Endpoint routing |
| 229 | + |
| 230 | +```fsharp [1, 6] |
| 231 | +let endpoints = |
| 232 | + [] // a list of enpoints |
| 233 | +let configureApp (appBuilder : IApplicationBuilder) = |
| 234 | + appBuilder |
| 235 | + .UseRouting() |
| 236 | + .UseEndpoints(fun e -> e.MapGiraffeEndpoints(endpoints)) |
| 237 | + |> ignore |
| 238 | +``` |
| 239 | + |
| 240 | +note: |
| 241 | +```fsharp |
| 242 | +let endpoints = |
| 243 | + [ |
| 244 | + GET [ |
| 245 | + route "/" (text "Hello World") |
| 246 | + routef "/%s/%i" handler2 |
| 247 | + routef "/%s/%s/%s/%i" handler3 |
| 248 | + ] |
| 249 | + subRoute "/sub" [ |
| 250 | + // Not specifying a http verb means it will listen to all verbs |
| 251 | + route "/test" handler1 |
| 252 | + ] |
| 253 | + ] |
| 254 | +``` |
18 | 255 |
|
19 | 256 | ----
|
20 | 257 |
|
21 | 258 | ## References
|
22 | 259 |
|
| 260 | +* [Reasoning behind creating Giraffe](https://dusted.codes/functional-aspnet-core) |
| 261 | + |
0 commit comments