haskell - 每个请求数据的中间件
问题描述
在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))))
my-header
在这个中间件中,我正在检查 in中是否有非零值:headers
,如果是,我将在request
地图中附加一些数据。这表明我可以将request
和response
视为有点“有状态”的数据。
我还是 haskell 的新手,想用scotty
. 在查看了中间件的类型之后,我可以创建一个这样的中间件:
myMiddleware :: Middleware
myMiddleware app req respond = app req respond
盯着类型看了半天,还是不知道该怎么做。一些阅读和思考使我认为这是不可能的,Middleware
只能使处理程序短路和/或更改生成的响应。这是真的?
解决方案
这也让我困惑了很久!但是弄清楚它给了我一个有用的技术来理解 Haskell 库类型。
首先,我将从未定义的中间件开始:
myMiddleware :: Middleware
myMiddleware = undefined
那么是什么Middleware
?关键是看类型的定义:
type Middleware = Application -> Application
让我们从第一层(或抽象层)开始,让中间件获取一个应用程序并返回一个应用程序。我们不知道如何修改应用程序,所以我们现在将准确返回传入的内容。
myMiddleware :: Application -> Application
myMiddleware theOriginalApp = theOriginalApp
但什么是应用程序?再次,让我们转向 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
在函数定义中扩展为与返回类型匹配的 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 与该返回函数的类型相匹配,该函数接受一个 Request 和一个函数(我们不关心)并返回一个IO ResponseReceived
.
所以,我们需要一些可以使用myModifiedRequest
和返回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
推荐阅读
- laravel - 此密码重置令牌无效 laravel 7
- c# - Xamarin.Forms 动态链接库调试问题
- node.js - 如何为电子邮件定义一个模式,以便它接受数字以及特殊字符和字符串,也使用猫鼬
- vert.x - 昂贵的 Vertx Eventbus Json 处理
- javascript - 使用异步调用设置的 JS 变量的范围
- r - 我希望我的函数返回 TRUE 或 FALSE,具体取决于种群是否灭绝到最大时间
- python - 从使用 filter_by 的 Pandas DataFrame 返回 2 个特定列
- python - 在 Numpy 中,如何使用大小为 B 的索引数组来索引 B x N x M 数组以产生 B x M 数组?
- c - 如何在Windows上计算C中文件夹中的元素数
- python - 从 powershell 打开 jupyter notebook 时如何解决此错误?