2011-07-06 15 views
19

Jeśli mam prośbę "size=3&mean=1&sd=3&type=pdf&distr=normal" Jaka jest idiomatycznych sposób pisania funkcji (defn request->map [request] ...) że trwa to żądanie i Zwraca mapę {:size 3, :mean 1, :sd 3, :type pdf, :distr normal}Jak analizować parametry URL w Clojure?

Oto moja próba (przy użyciu clojure.walk i clojure.string):

(defn request-to-map 
    [request] 
    (keywordize-keys 
     (apply hash-map 
      (split request #"(&|=)")))) 

Jestem zainteresowany tym, jak inni rozwiązaliby ten problem.

Odpowiedz

10

Można to zrobić łatwo z kilku bibliotek Java. Byłbym niezdecydowany, aby spróbować przetasować mój własny parser, chyba że dokładnie przeczytam specyfikacje URI i upewniłem się, że nie brakuje mi żadnych przypadków krawędzi (np. Params pojawiające się w zapytaniu dwa razy z różnymi wartościami). Wykorzystuje 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"} 
+0

Należy zauważyć, że w ciągu roku klasa UrlEncoded została zmieniona, a powyższy kod nie działa w środowisku Jetty 9.2.10. Działa: (defn- analizy składniowej, zapytań ciąg [zapytanie] (- >> zapytanie urlencoded ({w}) (clojure.algo.generic..functor/fmap first))) – skrii

3

Twoja wygląda dobrze. Staram się nadużywać regexes, więc bym rozwiązał go jako

(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"} 

EDIT: starają się trzymać z dala od clojure.walk chociaż. Nie sądzę, że jest to oficjalnie przestarzałe, ale nie jest zbyt dobrze utrzymane. (Ja też go używam, więc nie czuj się tak źle).

+0

Nie polecam tego prostego wyrażenia jako sposobu na rozwiązanie problemu, ale próbuję ujawnić ci kilka interesujących idiomów, takich jak 'do ... dla' i' powtórzyć seq '. – amalloy

+0

thx za odpowiedź, wciąż próbuję zrozumieć co robi twój kod, także próbuję zrobić to jako: rozmiar 1 zamiast: rozmiar "1" używając Integer/parseInt chociaż nie w pełni wpasowałem to do mojego jeszcze kod: – ChrisR

+0

Powinieneś także wpisać URLDecode, nie? –

16

Zakładając chcesz przeanalizować parametry zapytania żądania HTTP, to dlaczego nie skorzystać ring? ring.middleware.params zawiera to, co chcesz.

Funkcja ekstrakcji parametrów idzie tak:

(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 #"&"))) 
13

Korzystanie form-decode i keywordize-keys:

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

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

{:foo "bar", :hello "world"} 
+0

Najprostsza odpowiedź przy użyciu pierścienia 1.2 –

3

mogą również korzystać z tej biblioteki zarówno Clojure i 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"} 
0

natknąłem to pytanie przy konstruowaniu mój własna strona i odpowiedź może być nieco inna i łatwiejsza, jeśli przekazujesz parametry wewnętrznie.

Korzystanie Sekretarza obsługiwać routing: https://github.com/gf3/secretary

Parametry są automatycznie rozpakowany na mapie: zapytanie-params gdy mecz trasa została znaleziona. Przykład podany w dokumentacji:

(defroute "https://stackoverflow.com/users/:id" [id query-params] 
    (js/console.log (str "User: " id)) 
    (js/console.log (pr-str query-params))) 

(defroute #"https://stackoverflow.com/users/(\d+)" [id {:keys [query-params]}] 
    (js/console.log (str "User: " id)) 
    (js/console.log (pr-str query-params))) 

;; In both instances... 
(secretary/dispach! "https://stackoverflow.com/users/10?action=delete") 
;; ... will log 
;; User: 10 
;; "{:action \"delete\"}"