最近在写Clojure web的后台。用的是ring,前端还可以。看看说明文档就可以搞了。后端,真心不懂怎么调用的。所以看了一下源代码。
做了一点注释。分析一下。
(ns ring.util.servlet
"Compatibility functions for turning a ring handler into a Java servlet."
(:require [clojure.java.io :as io]
[clojure.string :as string])
(:import (java.io File InputStream FileInputStream) ; 文件处理类
(javax.servlet.http HttpServlet
HttpServletRequest
HttpServletResponse))) ; http处理类
(defn- get-headers
"Creates a name/value map of all the request headers." ; 创建请求映射
[^HttpServletRequest request]
(reduce
(fn [headers, ^String name]
(assoc headers
(.toLowerCase name) ; 全部小写
(->> (.getHeaders request name) ; 返回指定的请求
(enumeration-seq) ; 返回枚举序列
(string/join ","))))
{}
(enumeration-seq (.getHeaderNames request)))) ; 返回处理过的序列
(defn- get-content-length
"Returns the content length, or nil if there is no content." ; 获得内容大小
[^HttpServletRequest request]
(let [length (.getContentLength request)]
(if (>= length 0) length)))
(defn get-client-cert
"Returns the SSL client certificate of the reqest, if one exists." ; 获得SSL证书
[^HttpServletRequest request]
(first (.getAttribute request "javax.servlet.request.X509Certificate")))
(defn build-request-map
"Create the request map from the HttpServletRequest object." ; 创建来自HttpServletRequest对象
[^HttpServletRequest request]
{:server-port (.getServerPort request)
:server-name (.getServerName request)
:remote-addr (.getRemoteAddr request)
:uri (.getRequestURI request)
:query-string (.getQueryString request)
:scheme (keyword (.getScheme request))
:request-method (keyword (.toLowerCase (.getMethod request)))
:headers (get-headers request)
:content-type (.getContentType request)
:content-length (get-content-length request)
:character-encoding (.getCharacterEncoding request)
:ssl-client-cert (get-client-cert request)
:body (.getInputStream request)}) ; 内容
(defn merge-servlet-keys
"Associate servlet-specific keys with the request map for use with legacy
systems." ; 包装servlet类 形式是映射 给其他调用
[request-map
^HttpServlet servlet
^HttpServletRequest request
^HttpServletResponse response]
(merge request-map
{:servlet servlet
:servlet-request request
:servlet-response response
:servlet-context (.getServletContext servlet)})) ; 上下文
(defn set-status
"Update a HttpServletResponse with a status code." ; 响应状态设置
[^HttpServletResponse response, status]
(.setStatus response status))
(defn set-headers
"Update a HttpServletResponse with a map of headers."
[^HttpServletResponse response, headers]
(doseq [[key val-or-vals] headers]
(if (string? val-or-vals) ; 字符串格式
(.setHeader response key val-or-vals)
(doseq [val val-or-vals] ; 映射格式
(.addHeader response key val))))
; Some headers must be set through specific methods
(when-let [content-type (get headers "Content-Type")] ; 条件绑定到content-type
(.setContentType response content-type)))
(defn- set-body
"Update a HttpServletResponse body with a String, ISeq, File or InputStream." ; 更新内容
[^HttpServletResponse response, body]
(cond
(string? body) ; 字符串
(with-open [writer (.getWriter response)]
(.print writer body))
(seq? body) ; 序列
(with-open [writer (.getWriter response)]
(doseq [chunk body]
(.print writer (str chunk))
(.flush writer)))
(instance? InputStream body) ; 输入流
(with-open [^InputStream b body]
(io/copy b (.getOutputStream response)))
(instance? File body) ; 对象
(let [^File f body]
(with-open [stream (FileInputStream. f)]
(set-body response stream)))
(nil? body)
nil
:else
(throw (Exception. ^String (format "Unrecognized body: %s" body)))))
(defn update-servlet-response
"Update the HttpServletResponse using a response map." ; 更新输出内容
[^HttpServletResponse response, {:keys [status headers body]}]
(when-not response
(throw (Exception. "Null response given.")))
(when status
(set-status response status))
(doto response
(set-headers headers)
(set-body body)))
(defn make-service-method
"Turns a handler into a function that takes the same arguments and has the
same return value as the service method in the HttpServlet class." ; 服务方法
[handler]
(fn [^HttpServlet servlet
^HttpServletRequest request
^HttpServletResponse response]
(let [request-map (-> request
(build-request-map)
(merge-servlet-keys servlet request response))]
(if-let [response-map (handler request-map)] ; 关键在于用handler来处理request-map 并且把结果更新到response中
(update-servlet-response response response-map)
(throw (NullPointerException. "Handler returned nil"))))))
(defn servlet
"Create a servlet from a Ring handler.." ; 从处理器创建servlet
[handler]
(proxy [HttpServlet] []
(service [request response]
((make-service-method handler)
this request response))))
(defmacro defservice
"Defines a service method with an optional prefix suitable for being used by
genclass to compile a HttpServlet class.
e.g. (defservice my-handler)
(defservice \"my-prefix-\" my-handler)"
([handler]
`(defservice "-" ~handler))
([prefix handler]
`(defn ~(symbol (str prefix "service"))
[servlet# request# response#]
((make-service-method ~handler)
servlet# request# response#))))
其实写Clojure还是很清楚的。没有想象当中那么复杂。