Как разобрать параметры URL в Clojure? - PullRequest
19 голосов
/ 06 июля 2011

Если у меня есть запрос "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 ]

16 голосов
/ 06 июля 2011

Если вы хотите проанализировать параметры запроса 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 #"&")))
15 голосов
/ 25 июня 2013

Использование form-decode и keywordize-keys:

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

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

{:foo "bar", :hello "world"}
10 голосов
/ 07 июля 2011

Вы можете сделать это легко с рядом библиотек 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 голосов
/ 06 ноября 2013

Можно также использовать эту библиотеку как для 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"}
3 голосов
/ 06 июля 2011

Ваш выглядит хорошо. Я склонен злоупотреблять регулярными выражениями, поэтому решил бы это как

(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. Я не думаю, что это официально устарело, но в не очень хорошем состоянии. (Однако я тоже этим пользуюсь, так что не расстраивайся).

0 голосов
/ 18 апреля 2018

Вы можете использовать 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 объекте с именем :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.

0 голосов
/ 25 июля 2014

Я сталкивался с этим вопросом при создании собственного сайта, и ответ может быть немного другим и более простым, если вы передаете параметры внутренне.

Использование секретаря для обработки маршрутизации: 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\"}"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...