ring.util.servlet 简短注释

最近在写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还是很清楚的。没有想象当中那么复杂。

posted @ 2013-04-15 11:15  snakevash  阅读(302)  评论(0编辑  收藏  举报