Clojure 为什么环中间件的顺序需要颠倒?
我正在为Ring编写一些中间件,我真的很困惑,为什么我必须颠倒中间件的顺序 我发现了这个,但它不能解释我为什么要反转它 下面是这篇博文的简短摘录:Clojure 为什么环中间件的顺序需要颠倒?,clojure,ring,Clojure,Ring,我正在为Ring编写一些中间件,我真的很困惑,为什么我必须颠倒中间件的顺序 我发现了这个,但它不能解释我为什么要反转它 下面是这篇博文的简短摘录: (def app (wrap-keyword-params (wrap-params my-handler))) 答复是: {; Trimmed for brevity :params {"my_param" "54"}} 请注意,由于params散列尚不存在,因此没有对其调用wrap关键字params。但当您颠倒中间件的顺序时,如下所示:
(def app
(wrap-keyword-params (wrap-params my-handler)))
答复是:
{; Trimmed for brevity
:params {"my_param" "54"}}
请注意,由于params散列尚不存在,因此没有对其调用wrap关键字params。但当您颠倒中间件的顺序时,如下所示:
(def app
(wrap-params (wrap-keyword-params my-handler)))
{; Trimmed for brevity
:params {:my_param "54"}}
它起作用了
有人能解释一下为什么要颠倒中间件的顺序吗?环形中间件是一系列函数,当堆叠起来时,返回一个处理函数 文章中回答您问题的部分: 在环包装的情况下,通常我们有“之前”的装饰 在调用“真正的”业务函数之前执行一些准备工作。 因为它们是高阶函数,而不是直接函数调用, 它们是按相反的顺序应用的。如果一个依赖另一个,那么 一个人需要依靠“内在” 下面是一个人为的例子:
(let [post-wrap (fn [handler]
(fn [request]
(str (handler request) ", post-wrapped")))
pre-wrap (fn [handler]
(fn [request]
(handler (str request ", pre-wrapped"))))
around (fn [handler]
(fn [request]
(str (handler (str request ", pre-around")) ", post-around")))
handler (-> (pre-wrap identity)
post-wrap
around)]
(println (handler "(this was the input)")))
这将打印并返回:
(this was the input), pre-around, pre-wrapped, post-wrapped, post-around
nil
您可能知道,ring
app
实际上只是一个函数,它接收请求
映射并返回响应
映射
在第一种情况下,应用功能的顺序如下:
request -> [wrap-keyword-params -> wrap-params -> my-handler] -> response
在请求
中查找键:params
,但它不在那里,因为有人根据“查询字符串和表单正文中的URL编码参数”添加该键
当您颠倒这两个的顺序时:
request -> [wrap-params -> wrap-keyword-params -> my-handler] -> response
因为一旦
请求
到达wrap关键字参数
,wrap参数
已经添加了相应的键,因此可以得到所需的结果。这有助于可视化中间件实际上是什么
(defn middleware [handler]
(fn [request]
;; ...
;; Do something to the request before sending it down the chain.
;; ...
(let [response (handler request)]
;; ...
;; Do something to the response that's coming back up the chain.
;; ...
response)))
对我来说,这是一个非常开心的时刻
乍一看,令人困惑的是中间件没有应用于请求,这正是您所想的
回想一下,Ring应用程序只是一个接受请求并返回响应的函数(这意味着它是一个处理程序):
让我们缩小一点。我们得到另一个处理程序:
((GET "/" [] "Hello") request) ;=> response
让我们再缩小一点。我们找到my routes
处理程序:
(my-routes request) ;=> response
那么,如果您想在将请求发送到my routes
处理程序之前做些什么呢?您可以用另一个处理程序包装它
((fn [req] (println "Request came in!") (my-routes req)) request) ;=> response
这有点难读,所以让我们先说清楚。我们可以定义一个返回该处理程序的函数。中间件是接受一个处理程序并将其包装为另一个处理程序的函数。它不会返回响应。它返回一个可以返回响应的处理程序
(defn println-middleware [wrapped-func]
(fn [req]
(println "Request came in!")
(wrapped-func req)))
((println-middleware my-route) request) ;=> response
如果我们需要在println middleware
收到请求之前做一些事情,那么我们可以再次包装它:
((outer-middleware (println-middleware my-routes)) request) ;=> response
关键是my routes
,就像您的my handler
,是唯一一个实际将请求作为参数的命名函数
最后一个演示:
(handler3 (handler2 (handler1 request))) ;=> response
((middleware1 (middleware2 (middleware3 handler1))) request) ;=> response
我写这么多是因为我能同情。但是,回到我的第一个
中间件示例,希望它更有意义。如果可以的话,我会给你10票,这很有帮助。这与底座类似,但功能一应俱全。
(handler3 (handler2 (handler1 request))) ;=> response
((middleware1 (middleware2 (middleware3 handler1))) request) ;=> response