如果我有请求"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 #"(&|=)"))))
我对其他人如何解决这个问题很感兴趣。
答案 0 :(得分:16)
假设您要解析HTTP请求查询参数,为什么不使用ring? ring.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-decode
和keywordize-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))))
的依赖。