每个请求数据的中间件
Middleware for per-request data
在clojure
中,我可以这样写:
(defn wrap-my-header
[handler]
(fn [request]
(let [request (if (get-in request [:headers "my-header"])
(assoc request :has-my-header? true)
request)]
(handler request))))
在这个中间件中,我正在检查 :headers
中的 my-header
中是否有非零值,如果是,我将在 request
中附加一些数据地图。这表明我可以将 request
和 response
视为有点 "stateful" 的数据。
我还是 haskell 的新手,想用 scotty
做类似的事情。查看 middleware 的类型后,我可以创建一个这样的中间件:
myMiddleware :: Middleware
myMiddleware app req respond = app req respond
盯着字体看了半天,还是不知道怎么做。一些阅读和思考让我认为这是不可能的,Middleware
只能使处理程序短路 and/or 来改变生成的响应。这是真的吗?
这也让我困惑了很久!但是弄清楚它给了我一个有用的技术来理解 Haskell 库类型。
首先,我将从未定义的中间件开始:
myMiddleware :: Middleware
myMiddleware = undefined
那么 Middleware
是什么?关键是看definition of the type:
type Middleware = Application -> Application
让我们从第一层(或抽象级别)开始,让中间件接受一个应用程序和 return 一个应用程序。我们不知道如何修改应用程序,所以我们 return 目前传入的内容。
myMiddleware :: Application -> Application
myMiddleware theOriginalApp = theOriginalApp
但什么是应用程序?同样,让我们 turn to Hackage:
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
一个应用程序就是一个函数!我们可能不确切知道每个部分应该做什么或应该是什么,但我们可以找出答案。让我们将类型签名中的 Application
替换为函数类型:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = theOriginalApp
现在我们可以看到这个类型应该允许我们访问一个Request
!但是我们如何使用它呢?
我们可以将函数定义中的theOriginalApp
扩展成一个匹配return类型的lambda表达式:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse -> undefined)
我们现在可以对请求做任何我们想做的事情了:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse ->
let myModifiedRequest = addSomeHeadersIfMissing req in
undefined)
那么 undefined
呢?好吧,我们正在尝试将我们的 lambda 与那个 return 函数的类型相匹配,它接受一个 Request 和一个函数(我们不关心)和 returns 一个 IO ResponseReceived
.
所以,我们需要一些可以使用 myModifiedRequest
和 return 和 IO ResponseReceived
的东西。幸运的是,我们的类型签名表明 theOriginalApp
具有正确的类型!为了让它适合,我们只需要给它一个 sendResponse
函数。
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse ->
let myModifiedRequest = addSomeHeadersIfMissing req in
theOriginalApp myModifiedRequest sendResponse)
就是这样,这会奏效!我们可以通过将类型注释简化回 Middleware
并去掉 lambda 来提高可读性。 (我们也可以 eta-reduce 并从参数和定义中删除 sendResponse
项,但我认为如果保留它会更清楚。)
结果:
myMiddleware :: Middleware
myMiddleware theOriginalApp req sendResponse =
let myModifiedRequest = addSomeHeadersIfMissing req in
theOriginalApp myModifiedRequest sendResponse
在clojure
中,我可以这样写:
(defn wrap-my-header
[handler]
(fn [request]
(let [request (if (get-in request [:headers "my-header"])
(assoc request :has-my-header? true)
request)]
(handler request))))
在这个中间件中,我正在检查 :headers
中的 my-header
中是否有非零值,如果是,我将在 request
中附加一些数据地图。这表明我可以将 request
和 response
视为有点 "stateful" 的数据。
我还是 haskell 的新手,想用 scotty
做类似的事情。查看 middleware 的类型后,我可以创建一个这样的中间件:
myMiddleware :: Middleware
myMiddleware app req respond = app req respond
盯着字体看了半天,还是不知道怎么做。一些阅读和思考让我认为这是不可能的,Middleware
只能使处理程序短路 and/or 来改变生成的响应。这是真的吗?
这也让我困惑了很久!但是弄清楚它给了我一个有用的技术来理解 Haskell 库类型。
首先,我将从未定义的中间件开始:
myMiddleware :: Middleware
myMiddleware = undefined
那么 Middleware
是什么?关键是看definition of the type:
type Middleware = Application -> Application
让我们从第一层(或抽象级别)开始,让中间件接受一个应用程序和 return 一个应用程序。我们不知道如何修改应用程序,所以我们 return 目前传入的内容。
myMiddleware :: Application -> Application
myMiddleware theOriginalApp = theOriginalApp
但什么是应用程序?同样,让我们 turn to Hackage:
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
一个应用程序就是一个函数!我们可能不确切知道每个部分应该做什么或应该是什么,但我们可以找出答案。让我们将类型签名中的 Application
替换为函数类型:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = theOriginalApp
现在我们可以看到这个类型应该允许我们访问一个Request
!但是我们如何使用它呢?
我们可以将函数定义中的theOriginalApp
扩展成一个匹配return类型的lambda表达式:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse -> undefined)
我们现在可以对请求做任何我们想做的事情了:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse ->
let myModifiedRequest = addSomeHeadersIfMissing req in
undefined)
那么 undefined
呢?好吧,我们正在尝试将我们的 lambda 与那个 return 函数的类型相匹配,它接受一个 Request 和一个函数(我们不关心)和 returns 一个 IO ResponseReceived
.
所以,我们需要一些可以使用 myModifiedRequest
和 return 和 IO ResponseReceived
的东西。幸运的是,我们的类型签名表明 theOriginalApp
具有正确的类型!为了让它适合,我们只需要给它一个 sendResponse
函数。
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse ->
let myModifiedRequest = addSomeHeadersIfMissing req in
theOriginalApp myModifiedRequest sendResponse)
就是这样,这会奏效!我们可以通过将类型注释简化回 Middleware
并去掉 lambda 来提高可读性。 (我们也可以 eta-reduce 并从参数和定义中删除 sendResponse
项,但我认为如果保留它会更清楚。)
结果:
myMiddleware :: Middleware
myMiddleware theOriginalApp req sendResponse =
let myModifiedRequest = addSomeHeadersIfMissing req in
theOriginalApp myModifiedRequest sendResponse