首先要说明的是:clojure ring不是新思想,根据其git首页的描述,是从Python‘s WSGI and Ruby‘s Rack获得思想。最近写了几星期的clojure代码,觉得clojure ring实在是太简单了(这是褒义),所以忍不住介绍一下。
先看一下SPEC:https://github.com/ring-clojure/ring/blob/master/SPEC
总结起来就是一句话:你收到一个map,返回一个map,你的Handler就做这件事情。
那么中间件呢?就是包裹一下handler,在handler之前做点事,在handler之后做点事。
下面是假象的handler代码:
;request比如 {:uri / :query-string "a=1"} (defn ahandler [request] {:status 200 :body "hello"} )
中间件(相当于java的filter类似)
(defn amiddleware [handler] (fn [request] (let [new-request (dosomething request) response (handler new-request)] ;如果要对response处理一下,就在这里处理。 )))
中间件的作用这里就举上面的例子,如果最终handler直接用query-string总归不太方便,那么可以在中间件里面用它产生一个新的key,比如paras,而paras是一个map,这样可以直接用(:a paras)获取a的值。当然ring是定义基础的约定,在它的上面就是形形色色的框架了。
不过写这篇短博客的主要目的还是为了提醒自己不要和自己已知的java相混淆。
clojure和java的习惯差异很大,在java的世界里,当你获得一个Request(Response)对象,你把它传给其他类去加工,不管经过几个步骤,你的Request还是原来的Request,你的Response还是原来的Response,只不过内容变化而已。
clojure是不可变为主,所以它的request和response是每经过一个步骤都是新的。
(defn shiro-body [handler request] (let [subject (build-subject request) session-before (.getSession subject false) response (.execute (build-callable)) session-after (.getSession (sec-util/get-subject))] (if (and (not session-before) session-after) (assoc-in response [:cookies :JSESSIONID] (create-cookie (.getId session-after))) (if (and (session-before) (not session-after)) (assoc-in response [:cookies :JSESSIONID] (create-cookie (.getId session-after) :http-only false :max-age 0)) response))))
上面的代码是我集成ring和apache shiro的一个middleware,刚开始的时候老是觉得修改了response,返回response即可。但是clojure的规则不是这样。
(let [response {:status 200 :body "hello"}] (assoc-in response [:cookies :JSESSIONID] "xxxooo") response)) ;这里就误会了,总是觉得response已经修改了,其实 (assoc-in response [:cookies :JSESSIONID] "xxxooo") 才是新的response。不要在最后加response! ;这样即可。 (let [response {:status 200 :body "hello"}] (assoc-in response [:cookies :JSESSIONID] "xxxooo"))
上面shiro-body的流程就是:
1、如果进来的时候subject没有session,出去的时候有了,说明经过了session操作,或者登录了,需要在cookie里面加上sessionid。
2、如果进来的时候subject有session,出去的时候没有了,说明session失效了,或者登出了,需要删除cookie
3、如果进来出去都没有,或者都有,就不作为。