如何解析Clojure中的URL参数?

时间:2011-07-06 04:36:46

标签: clojure

如果我有请求"size=3&mean=1&sd=3&type=pdf&distr=normal",那么编写接受此请求的函数(defn request->map [request] ...)的惯用方法是什么? 返回地图{:size 3, :mean 1, :sd 3, :type pdf, :distr normal}

这是我的尝试(使用clojure.walk和clojure.string):

(defn request-to-map
   [request]
   (keywordize-keys
      (apply hash-map
             (split request #"(&|=)"))))

我对其他人如何解决这个问题很感兴趣。

7 个答案:

答案 0 :(得分:16)

假设您要解析HTTP请求查询参数,为什么不使用ringring.middleware.params包含您想要的内容。

参数提取功能如下:

(defn- parse-params
  "Parse parameters from a string into a map."
  [^String param-string encoding]
  (reduce
    (fn [param-map encoded-param]
      (if-let [[_ key val] (re-matches #"([^=]+)=(.*)" encoded-param)]
        (assoc-param param-map
          (codec/url-decode key encoding)
          (codec/url-decode (or val "") encoding))
         param-map))
    {}
    (string/split param-string #"&")))

答案 1 :(得分:15)

使用form-decodekeywordize-keys

(use 'ring.util.codec)
(use 'clojure.walk)

(keywordize-keys (form-decode "hello=world&foo=bar"))

{:foo "bar", :hello "world"}

答案 2 :(得分:10)

您可以使用许多Java库轻松完成此操作。除非我仔细阅读URI规范并确保我没有遗漏任何边缘情况(例如,在查询中出现两次具有不同值的参数),否则我会犹豫是否尝试滚动自己的解析器。这使用jetty-util

(import '[org.eclipse.jetty.util UrlEncoded MultiMap])

(defn parse-query-string [query]
  (let [params (MultiMap.)]
    (UrlEncoded/decodeTo query params "UTF-8")
    (into {} params)))

user> (parse-query-string "size=3&mean=1&sd=3&type=pdf&distr=normal")
{"sd" "3", "mean" "1", "distr" "normal", "type" "pdf", "size" "3"}

答案 3 :(得分:3)

你的看起来很好。我倾向于过度使用正则表达式,所以我会把它解决为

(defn request-to-keywords [req]
  (into {} (for [[_ k v] (re-seq #"([^&=]+)=([^&]+)" req)]
    [(keyword k) v])))

(request-to-keywords "size=1&test=3NA=G")

{:size "1", :test "3NA=G"}

编辑:尽量远离clojure.walk。我不认为它已被正式弃用,但它维护得不是很好。 (不过我也用得很多,所以不要觉得太糟糕)。

答案 4 :(得分:3)

也可以将此库用于clojure和clojurescript:https://github.com/cemerick/url

user=> (-> "a=1&b=2&c=3" cemerick.url/query->map clojure.walk/keywordize-keys)
{:a "1", :b "2", :c "3"}

答案 5 :(得分:0)

我在构建自己的网站时遇到了这个问题,如果您在内部传递参数,答案可能会有所不同,而且更容易。

使用秘书处理路由:https://github.com/gf3/secretary

当找到路线匹配时,参数会自动提取到地图中:query-params。文档中给出的示例:

(defroute "/users/:id" [id query-params]
  (js/console.log (str "User: " id))
  (js/console.log (pr-str query-params)))

(defroute #"/users/(\d+)" [id {:keys [query-params]}]
  (js/console.log (str "User: " id))
  (js/console.log (pr-str query-params)))

;; In both instances...
(secretary/dispach! "/users/10?action=delete")
;; ... will log
;; User: 10
;; "{:action \"delete\"}"

答案 6 :(得分:0)

您可以使用ring.middleware.params。以下是aleph

的示例
user=> (require '[aleph.http :as http])
user=> (defn my-handler [req] (println "params:" (:params req)))
user=> (def server (http/start-server (wrap-params my-handler)))

wrap-params在名为request的{​​{1}}对象中创建一个条目。如果您希望查询参数为关键字,则可以使用:params。请务必先使用ring.middleware.keyword-params换行:

wrap-params

但是,请注意,这包括对user=> (require '[ring.middleware.params :refer [wrap-params]]) user=> (require '[ring.middleware.keyword-params :refer [wrap-keyword-params]) user=> (def server (http/start-server (wrap-keyword-params (wrap-params my-handler)))) 的依赖。