我们先创建一个handler
(ns middleware-example
(:use ring.adapter.jetty
ring.middleware.params))
(defn handler [request]
{:headers {}
:status 200
:body (str "Hello word!" )})
(def app
handler)
;; Start the server if it hasn't already been started
(defonce server (ring.adapter.jetty/run-jetty #'app {:port 7071 :join? false}))
我们看到,handler
会接收一个request参数,并返回一个hash-map,hash-map包括headers、status和body,这个hash-map就是一个response。
党我们访问 http://localhost:7071/ ,就会看到 Hello word!
用middleware处理response
(defn shout
[handler]
(fn [request]
(let [response (handler request)]
(update-in response [:body] clojure.string/upper-case)))) ;; 将response中的body转换成大写
(def app
(-> handler
shout))
当我们刷新页面的时候,结果就变成了 HELLO WORD!
这个例子说明,我们可以通过middleware改变response。
用middleware处理request
我们可以用middle更好的解析、处理request,请看下面的例子
(ns middleware-example
(:use ring.adapter.jetty
ring.middleware.params))
(defn print-query-params [handler]
(fn [request]
(println "In print-query-params, the params is:" (:query-params request))
(handler request)))
(defn handler [request]
(println "In handler, the params is:" (:query-params request))
(let [name ((:query-params request) "name")]
{:headers {}
:status 200
:body (str "Hello " name "!")}))
(def app
(-> handler
wrap-params
print-query-params))
;; Start the server if it hasn't already been started
(defonce server (ring.adapter.jetty/run-jetty #'app {:port 7071 :join? false}))
访问 http://localhost:7071?name=mike 后,在console里我们将看到
In print-query-params, the params is: nil
In handler, the params is: {name mike}
这段代码可以说明,params是被wrap-params
所解析出来的,request在创给wrap-params
、handler
前,先被传给了print-query-params
。
(在浏览器内,我们实际上会看到两次输出,是因为浏览器在请求url的时候,还请求了favoicon,chrome每次刷新都是两个请求,而safari只有第一次请求了favoicon,之后刷新都没有再次请求)
Middleware
首先,我们将handler这个参数传给middleware,这个middleware会返回一个方法,
这个方法会接收一个request
参数,我们可以在这个方法里生成新的request
,并把这个request传给handler(handler new-request)
,得到返回后,我们又可以对response
进行操作。
通过app的定义(-> handler wrap-params print-query-params)
,我们知道,middleware就像洋葱皮一样,一层层包裹handler。
最后面的包裹在最外层,request一层层通过middleware,每一层都可以对request进行操作,最后handler 会根据最终的request生成response,然后response又一层层传出middleware,每一层middleware有有机会对生成的response做处理。
就像下图一样(注意,后面的middleware,处于最外层)。
其他框架的中间件
rack也是使用一样的模型处理中间件。rack使用use
方法注册middleware,并将注册的middleware放入@use
这个实例变量里。
rack有一个to_app
的方法,在这个方法内@use.reverse.inject(app) { |a,e| e[a] }
,把最先注册的middleware放在最外层,并包裹好。
ring、rack、koa的middleware都是使用洋葱模型,但express的middleware却不一样,middleware只是一层一层传下去的,比如要记录请求时间,就需要写两个中间件。
var app = express()
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
小结
Middleware可以用来对response和request进行处理(比如解析params),也可以中断执行(比如过滤ip)等。
但需要注意middleware的顺序,最后的middleware被放在了最外层。最外层,最先处理request,最后处理response。
参考
https://codenoble.com/blog/un...
https://expressjs.com/en/guid...
http://liujiacai.net/blog/201...