Как анализировать параметры URL в 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 #"(&|=)"))))
Мне интересно, как другие решат эту проблему.
Ответы
Ответ 1
Вы можете сделать это легко с помощью ряда библиотек 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"}
Ответ 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
Используя form-decode
и keywordize-keys
:
(use 'ring.util.codec)
(use 'clojure.walk)
(keywordize-keys (form-decode "hello=world&foo=bar"))
{:foo "bar", :hello "world"}
Ответ 4
Ты выглядишь прекрасно. Я склонен злоупотреблять регулярными выражениями, поэтому я решил бы это как
(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. Я не думаю, что это официально устарело, но оно не очень хорошо поддерживается. (Я тоже слишком много использую это, поэтому не чувствую себя слишком плохо).
Ответ 5
Можно также использовать эту библиотеку для 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"}
Ответ 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\"}"