[hunchentoot-devel] multiple POST/GET values

Jaap de Heer jaap at streamtech.nl
Mon Jul 21 18:54:41 UTC 2008


Howdy,

I ran into trouble with Hunchentoot trying to process the result
of a form containing a select multiple:
<select multiple name="foo-or-bar">
  <option value="foo">Foo
  <option value="bar">Bar
</select>

The user can select both foo and bar, which results in both being
submitted.
In the POST request, this looks something like:
"other-field-1=x&foo-or-bar=foo&foo-or-bar=bar&other-field-2=x"

Hunchentoot will pick up on only one of them, i.e.
(get-post-parameter "foo-or-bar") will be either "foo" or
"bar" but not both; it should be both.

I've baked a patch for this that will make (get-post-parameter)
et al return a list containing all submitted values for this
field, if there is more than 1 value; otherwise, it just returns
the string as usual.
My Lisp fu is limited, and this is only one of a few possible
solutions, so feel free to write a better one ;-)

BTW. I suppose the same problem and fix applies for multiple
checkboxes with the same names, and (though this may be invalid)
even multiple <input> fields with the same name.

Cheers,

Jaap


--- util.lisp.org       2008-07-21 20:29:31.000000000 +0200
+++ util.lisp   2008-07-21 20:29:57.000000000 +0200
@@ -168,6 +168,25 @@
     (loop for code across (md5:md5sum-sequence string)
          do (format s "~2,'0x" code))))

+(defun group-by (lst &key (key #'identity) (test #'eq))
+   "Transform a sorted list into a list of lists, grouping together elements with equal key."
+   (labels ((lp (lst result current-group)
+              (cond ((null lst) (nreverse (if (null current-group)
+                                              result
+                                              (cons (nreverse current-group) result))))
+                    ((or (null current-group)
+                         (funcall test
+                                  (funcall key (first lst))
+                                  (funcall key (first current-group))))
+                     (lp (rest lst)
+                         result
+                         (cons (first lst) current-group)))
+                    (t
+                     (lp (rest lst)
+                         (cons (nreverse current-group) result)
+                         (list (first lst)))))))
+     (lp lst nil nil)))
+
 (defun escape-for-html (string)
   "Escapes the characters #\\<, #\\>, #\\', #\\\", and #\\& for HTML output."
   (with-output-to-string (out)
@@ -281,12 +300,18 @@
                                        &optional (external-format *hunchentoot-default-external-format*))
   "Converts a list FORM-URL-ENCODED-LIST of name/value pairs into an
 alist.  Both names and values are url-decoded while doing this."
-  (mapcar #'(lambda (entry)
-              (destructuring-bind (name &optional value)
-                  (split "=" entry :limit 2)
-                (cons (string-trim " " (url-decode name external-format))
-                      (url-decode (or value "") external-format))))
-          form-url-encoded-list))
+  (mapcar (lambda (x) (cons (caar x)
+                            (if (> (length x) 1)
+                                (mapcar #'cdr x)
+                                (cdar x))))
+          (group-by (sort (mapcar #'(lambda (entry)
+                                      (destructuring-bind (name &optional value)
+                                          (split "=" entry :limit 2)
+                                        (cons (string-trim " " (url-decode name external-format))
+                                              (url-decode (or value "") external-format))))
+                                  form-url-encoded-list)
+                          #'string< :key #'car)
+                    :key #'car :test #'equal)))

 (defun url-encode (string &optional (external-format *hunchentoot-default-external-format*))
   "URL-encodes a string using the external format EXTERNAL-FORMAT."




More information about the Tbnl-devel mailing list