Ring 提供了 web 开发所需的基础构件,好比处理请求参数,cookie, session 等等。经过向 http-kit 或 jetty 注册 handler 的方式来提供服务。handler 函数接收一个 request 参数,此参数由调用者传递(此处为 http-kit 或 jetty)。然而 http-kit 或 jetty 传递过来的 request 参数只包含了基本标准键,Ring 将经过中间件的方式提供更高级的功能。web
如下为 request 包含的基本标准键:服务器
:server-port ---------- 用于处理该请求的服务端口 :server-name ---------- 服务器的 IP 地址或是主机名 :remote-addr ---------- 客户端的 IP 地址 :query-string --------- 请求的查询字符串 :scheme --------------- 协议的类型,能够是 HTTP 或者 HTTPS :request-method ------- 请求的方法,好比::get、:head、:options、:put、:post 或 :delete :request-string ------- 请求的查询字符串 :content-type --------- 请求消息体的 MIME 类型 :content-length ------- 请求消息体的字节数 :character-encoding --- 请求采用的字符编码名称 :headers -------------- 包含了请求头部的map :body ----------------- 可用于读取请求消息体的输入流 :context -------------- 当应用没有做为根来部署时,其所处的上下文 :uri ------------------ 服务端的 URI 全路径,包含了 :context(若是存在)的部分 :ssl-client-cert ------ 客户端的 SSL 证书 * 注意:此处列出的键,并不必定会出如今全部的请求中,好比 :ssl-client-cert
Ring 中间件的原理其实很简单。链式执行,前一个的输出做为下一个的输入,有点相似于管道。在深刻讲解 Ring 的中间件前,咱们先来熟悉 Clojure 提供的一个方法:->。cookie
方法 -> 就支持链式执行,它接受任意多个参数。第一个参数能够为任意形式,剩下的参数必须为函数,而且至少接受一个参数。这是由于方法 -> 实现的功能是:将前一个表达式的结果做为下一个函数的第一个参数来调用。执行顺序是先上后下,举例以下:session
(defn test-a [msg] (let [_msg (str msg " -- a")] (println _msg) _msg)) (defn test-b [msg] (let [_msg (str msg " -- b")] (println _msg) _msg)) (defn test-c [msg] (let [_msg (str msg " -- c")] (println _msg) _msg)) (defn -main [& args] (-> (test-a "<testing>") test-b test-c))
在命令行执行:闭包
$ lein run <testing> -- a <testing> -- a -- b <testing> -- a -- b -- c
能够看到,前一个表达式的值做为下一个函数的第一个参数。这种内置的语法令链式执行看起来一目了然,不需像其它语言那样层层闭包。固然若是 Clojure 没有内置这样的函数,也能够经过定义宏的方式来实现。app
Ring 中间件其实就是经过 -> 来进行链式执行。还记得须要向 http-kit 或 jetty 注册一个 handler 吗?Ring 中间件的原理说白了就是对 handler 进行层层包装,返回一个新的 handler。可是由于返回了一个新的 handler,也是就是闭包,对于真正的执行时机可能会有点不太同样。接下来举例说明:函数
(defn my-handler [req] (println "my-handler") "my-handler response") (defn test-a [handler] (fn [req] (println "test-a") (handler req))) (defn test-b [handler] (fn [req] (println "test-b") (handler req))) (defn test-c [handler] (fn [req] (println "test-c") (handler req))) (def app (-> my-handler test-a test-b test-c)) (defn -main [& args] (app nil))
在命令行执行:post
$ lein run test-c test-b test-a my-handler
结果看起来跟以前说的不太同样。以前说的执行顺序是先上后下,此次的结果明显是先下后上,到底是哪里出现问题了呢?其实两次的执行结果都是正确的。前一次是简单的链式执行,而这一次链式执行发生后,每一步都返回了一个闭包,致使了延迟执行。相似如下代码:编码
(-> my-handler test-a test-b test-c)
其实能够当作:spa
(test-c (test-b (test-a my-handler)))
展开后:
(println "test-c") ; test-b 返回的闭包 (println "test-b") ; test-a 返回的闭包 (println "test-a") ; my-handler (println "my-handler")
至此想必你已经对 Ring 的中间件有所了解了。
Ring 提供了一些基础的中间件,它们或多或少的对 request 和 response 进行修改,以达到特定目的。ring-devel 对开发环境提供了支持,好比 wrap-reload 容许你没必要重启便可在修改源码后自动从新载入等等。ring-core 提供了更多标准中间件,有wrap-params、wrap-not-modified、wrap-content-type等等。
Ring 中间件的执行流程相当重要,将来不少时候都要跟中间件打交道,好比自定义拦截,实现公共处理(其它语言中多是经过继承父控制器来实现)等等。
Ring 是一个很优秀的针对 web 开发的基础构件。研究它的原理和熟读它的源码不论是在 web 开发上仍是对于 Clojure 的理解上都有莫大的好处。