不理解 Suave API,总是返回相同的结果
not understanding the Suave API, always the same result is returned
这是一个测试:
open System
open System.Threading
open Newtonsoft.Json
open Suave
open Suave.Logging
open Suave.Operators
open Suave.Filters
open Suave.Writers
let private configuration = {
defaultConfig with
bindings = [ HttpBinding.createSimple HTTP "0.0.0.0" 80 ]
}
let private getServerTime () : WebPart =
DateTime.UtcNow |> JsonConvert.SerializeObject |> Successful.OK >=> setMimeType "application/json"
let private webApplication =
choose
[
GET >=> choose
[
path "/servertime" >=> getServerTime ()
]
]
let start () =
let listening, server = startWebServerAsync configuration webApplication
server |> Async.Start
listening |> Async.RunSynchronously |> ignore
[<EntryPoint>]
let main _ =
start()
Thread.Sleep(Timeout.Infinite)
0
在这个例子中,getServerTime 函数被调用一次,就是这样,每次对端点的后续调用都将 return 原始结果。
我不明白为什么?当我使用带参数的 pathScan 时,每次都会调用该函数,正如预期的那样,但在这种情况下,使用简单的 get,只有第一次调用被完成,而 this 被定义为一个函数。
但我也完全不理解文档(从文档的流程、内容和整体文档结构...),所以答案是可能很简单:)
首先,我强烈建议您学习单子组合。这是理解这些东西的必要基础。它会让您了解 >=>
和 >>=
是什么以及如何处理它们。
至于手头的问题:是的,您将 getServerTime
定义为一个函数,但这无关紧要,因为该函数在 [=17= 的构造过程中只被调用一次]值。
服务器的结构实际上是一个函数 HttpContext -> Async<HttpContext option>
。它获得一个请求上下文和 returns 它的修改版本。所有这些组合器 - choose
和 >=>
等等 - 它们都使用这样的函数。
表达式path "/servertime"
也是这样的函数。字面上地。你可以这样称呼它:
let httpCtx = ...
let newCtxAsync = path "/servertime" httpCtx
此外,表达式getServerTime()
也是这样的函数。所以你可以这样做:
let httpCtx = ...
let newCtxAsync = getServerTime () httpCtx
这就是 WebPart
类型。这是一个从上下文到新上下文的异步函数。
现在,>=>
运算符所做的就是组合这些功能。使它们通过管道将上下文从一个 Web 部件传递到下一个 Web 部件。就这些了。
当您编写 getServerTime
函数时,您创建了一个 WebPart
始终 return 相同的东西。有点像这样:
let f x y = printf "x = %d" x
let g = f 42
在这里,g
是一个函数(就像 WebPart
是一个函数),但无论何时调用它,它总是 return "x = 42"
。为什么?因为我部分应用了该参数,所以它现在有点“融入”g
的定义中。当前时间在您在 getServerTime
.
中创建的 WebPart
中“烘焙”的方式相同
如果您希望每次都return编辑不同的时间,您需要每次都重新创建 WebPart
。在每次调用时构造一个 new WebPart
,其中包含该调用的时间。要做到这一点的最小更改可能是:
let private getServerTime () : WebPart =
let time : WebPart = fun ctx -> (DateTime.UtcNow |> string |> Successful.OK) ctx
time >=> setMimeType "text/plain"
从表面上看,time
的定义可能很愚蠢:毕竟,let f x = g x
总是可以被 let f = g
替换,对吧?好吧,并非总是如此。只要 g
是纯的。但是你这里的webpart不是:要看当前时间。
这样,每次 time
webpart 是“运行”(这意味着它获取一个上下文作为参数)时,它将 运行 DateTime.UtcNow
,然后将其传递给 Successful.OK
,然后将上下文传递给结果函数。
这是一个测试:
open System
open System.Threading
open Newtonsoft.Json
open Suave
open Suave.Logging
open Suave.Operators
open Suave.Filters
open Suave.Writers
let private configuration = {
defaultConfig with
bindings = [ HttpBinding.createSimple HTTP "0.0.0.0" 80 ]
}
let private getServerTime () : WebPart =
DateTime.UtcNow |> JsonConvert.SerializeObject |> Successful.OK >=> setMimeType "application/json"
let private webApplication =
choose
[
GET >=> choose
[
path "/servertime" >=> getServerTime ()
]
]
let start () =
let listening, server = startWebServerAsync configuration webApplication
server |> Async.Start
listening |> Async.RunSynchronously |> ignore
[<EntryPoint>]
let main _ =
start()
Thread.Sleep(Timeout.Infinite)
0
在这个例子中,getServerTime 函数被调用一次,就是这样,每次对端点的后续调用都将 return 原始结果。
我不明白为什么?当我使用带参数的 pathScan 时,每次都会调用该函数,正如预期的那样,但在这种情况下,使用简单的 get,只有第一次调用被完成,而 this 被定义为一个函数。
但我也完全不理解文档(从文档的流程、内容和整体文档结构...),所以答案是可能很简单:)
首先,我强烈建议您学习单子组合。这是理解这些东西的必要基础。它会让您了解 >=>
和 >>=
是什么以及如何处理它们。
至于手头的问题:是的,您将 getServerTime
定义为一个函数,但这无关紧要,因为该函数在 [=17= 的构造过程中只被调用一次]值。
服务器的结构实际上是一个函数 HttpContext -> Async<HttpContext option>
。它获得一个请求上下文和 returns 它的修改版本。所有这些组合器 - choose
和 >=>
等等 - 它们都使用这样的函数。
表达式path "/servertime"
也是这样的函数。字面上地。你可以这样称呼它:
let httpCtx = ...
let newCtxAsync = path "/servertime" httpCtx
此外,表达式getServerTime()
也是这样的函数。所以你可以这样做:
let httpCtx = ...
let newCtxAsync = getServerTime () httpCtx
这就是 WebPart
类型。这是一个从上下文到新上下文的异步函数。
现在,>=>
运算符所做的就是组合这些功能。使它们通过管道将上下文从一个 Web 部件传递到下一个 Web 部件。就这些了。
当您编写 getServerTime
函数时,您创建了一个 WebPart
始终 return 相同的东西。有点像这样:
let f x y = printf "x = %d" x
let g = f 42
在这里,g
是一个函数(就像 WebPart
是一个函数),但无论何时调用它,它总是 return "x = 42"
。为什么?因为我部分应用了该参数,所以它现在有点“融入”g
的定义中。当前时间在您在 getServerTime
.
WebPart
中“烘焙”的方式相同
如果您希望每次都return编辑不同的时间,您需要每次都重新创建 WebPart
。在每次调用时构造一个 new WebPart
,其中包含该调用的时间。要做到这一点的最小更改可能是:
let private getServerTime () : WebPart =
let time : WebPart = fun ctx -> (DateTime.UtcNow |> string |> Successful.OK) ctx
time >=> setMimeType "text/plain"
从表面上看,time
的定义可能很愚蠢:毕竟,let f x = g x
总是可以被 let f = g
替换,对吧?好吧,并非总是如此。只要 g
是纯的。但是你这里的webpart不是:要看当前时间。
这样,每次 time
webpart 是“运行”(这意味着它获取一个上下文作为参数)时,它将 运行 DateTime.UtcNow
,然后将其传递给 Successful.OK
,然后将上下文传递给结果函数。