Подтвердить что ты не робот

Как анализировать параметры 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 #"(&|=)"))))

Мне интересно, как другие решат эту проблему.

4b9b3361

Ответ 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\"}"