2011-07-06 20 views
19

Si tengo la solicitud "size=3&mean=1&sd=3&type=pdf&distr=normal" cuál es la forma idiomática de escribir la función (defn request->map [request] ...) que toma esta solicitud y vuelve un mapa {:size 3, :mean 1, :sd 3, :type pdf, :distr normal}Cómo analizar los parámetros de URL en Clojure?

Aquí está mi intento (usando clojure.walk y clojure.string):

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

Estoy interesado en cómo otros resolverían este problema.

Respuesta

10

Usted puede hacer esto fácilmente con un número de bibliotecas Java. Sería reacio a intentar rodar mi propio analizador a menos que leyera las especificaciones URI cuidadosamente y me asegurara de que no me faltaban casos extremos (por ejemplo, params que aparecían en la consulta dos veces con diferentes valores). Esto utiliza 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

Tenga en cuenta que durante los años la clase UrlEncoded ha cambiado y el código anterior no funciona en Jetty 9.2.10. Esto funciona: (defn- análisis sintáctico-query-string [consulta] (- >> consulta urlencoded (en {}) (clojure.algo.generic..functor/fmap primero))) – skrii

3

El suyo se ve bien. Me tienden a abusar de expresiones regulares, por lo que habría resuelto como

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

Editar: tratar de mantenerse alejado de clojure.walk sin embargo. No creo que sea oficialmente obsoleto, pero no está muy bien mantenido. (Yo también lo uso mucho, así que no te sientas mal).

+0

No recomendar esta expresión regular bruto como una forma de resolver el problema, de verdad, pero tratando de quedar expuesto a algunos modismos interesantes como '... en for' y' re-ss '. – amalloy

+0

thx por respuesta, todavía estoy tratando de entender lo que hace tu código, también estoy tratando de hacerlo como: tamaño 1 en lugar de: tamaño "1" usando Integer/parseInt aunque no he trabajado completamente eso en mi código aún – ChrisR

+0

También debe URLDecode, ¿no? –

16

Suponiendo que desea analizar los parámetros de consulta de solicitud HTTP, ¿por qué no utilizar ring? ring.middleware.params contiene lo que quieres.

La función de extracción de parámetros dice así:

(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

Usando form-decode y keywordize-keys:

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

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

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

La respuesta más simple si se utiliza el anillo 1.2 –

3

También puede utilizar esta biblioteca para ambos clojure y 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

me encontré con esta pregunta en la construcción de mi propio sitio y la respuesta puede ser un poco diferente, y más fácil, si está pasando parámetros internamente.

Usando Secretario de manejar el enrutamiento: https://github.com/gf3/secretary

Los parámetros se extraen automáticamente a un mapa en: query-params cuando se encuentra una coincidencia ruta. El ejemplo dado en la documentación:

(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\"}" 
Cuestiones relacionadas