【问题标题】:How to parse URL parameters in Clojure?如何在 Clojure 中解析 URL 参数?
【发布时间】:2011-07-06 04:36:46
【问题描述】:

如果我有请求"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 #"(&|=)"))))

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

【问题讨论】:

    标签: clojure


    【解决方案1】:

    使用form-decodekeywordize-keys

    (use 'ring.util.codec)
    (use 'clojure.walk)
    
    (keywordize-keys (form-decode "hello=world&foo=bar"))
    
    {:foo "bar", :hello "world"}
    

    【讨论】:

    • 如果使用 ring 1.2 最简单的答案
    【解决方案2】:

    假设你要解析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 #"&")))
    

    【讨论】:

      【解决方案3】:

      您可以使用许多 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"}
      

      【讨论】:

      • 请注意,多年来 UrlEncoded 类发生了变化,上述代码在 Jetty 9.2.10 中不起作用。这有效: (defn- parse-query-string [query] (->> query UrlEncoded. (into {}) (clojure.algo.generic.functor/fmap first)))
      【解决方案4】:

      也可以将此库用于 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】:

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

        (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。我不认为它被正式弃用,但它的维护不是很好。 (不过,我也经常使用它,所以不要觉得太糟糕)。

        【讨论】:

        • 不推荐这种粗俗的正则表达式作为解决问题的方法,真的,而是试图让你接触一些有趣的成语,比如into...forre-seq
        • thx 回答,仍然试图了解您的代码的作用,我也试图使用 Integer/parseInt 将其设为 :size 1 而不是 :size "1" 虽然我没有还没有将它完全应用到我的代码中
        • @Eric 也许。如果您将此结果发送到的代码计划自行进行 URLDecode,那么您不应该这样做。
        【解决方案6】:

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

        使用秘书处理路由: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\"}"
        

        【讨论】:

          【解决方案7】:

          您可以使用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-paramsrequest 对象中创建一个名为: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))))
          

          但是,请注意,这包括对 ring 的依赖。

          【讨论】:

            猜你喜欢
            • 2017-04-22
            • 1970-01-01
            • 2022-12-20
            • 2011-01-28
            • 2014-10-28
            • 1970-01-01
            • 1970-01-01
            • 2012-01-19
            相关资源
            最近更新 更多