Query Parameters

Of course, generating HTML output is only half of Web programming. The other thing you need to do is get input from the user. As I discussed in the “A 30-Second Intro to Server-Side Web Programming” section, when a browser requests a page from a Web server, it can send query parameters in the URL and post data, both of which act as input to the server-side code.

AllegroServe, like most Web programming frameworks, takes care of parsing both these sources of input for you. By the time your published functions are called, all the key/value pairs from the query string and/or post data have been decoded and placed into an alist that you can retrieve from the request object with the function request-query. The following function returns a page showing all the query parameters it receives:

  1. (defun show-query-params (request entity)
  2. (with-http-response (request entity :content-type "text/html")
  3. (with-http-body (request entity)
  4. (with-html-output ((request-reply-stream request))
  5. (html
  6. (:standard-page
  7. (:title "Query Parameters")
  8. (if (request-query request)
  9. (html
  10. (:table :border 1
  11. (loop for (k . v) in (request-query request)
  12. do (html (:tr (:td k) (:td v))))))
  13. (html (:p "No query parameters.")))))))))
  14. (publish :path "/show-query-params" :function 'show-query-params)

If you give your browser a URL with a query string in it like the following:

  1. http://localhost:2001/show-query-params?foo=bar&baz=10

you should get back a page similar to the one shown in Figure 26-4.

Query Parameters - 图1

Figure 26-4. http://localhost:2001/show-query-params?foo=bar&baz=10

To generate some post data, you need an HTML form. The following function generates a simple form, which submits its data to show-query-params:

  1. (defun simple-form (request entity)
  2. (with-http-response (request entity :content-type "text/html")
  3. (with-http-body (request entity)
  4. (let ((*html-output* (request-reply-stream request)))
  5. (html
  6. (:html
  7. (:head (:title "Simple Form"))
  8. (:body
  9. (:form :method "POST" :action "/show-query-params"
  10. (:table
  11. (:tr (:td "Foo")
  12. (:td (:input :name "foo" :size 20)))
  13. (:tr (:td "Password")
  14. (:td (:input :name "password" :type "password" :size 20))))
  15. (:p (:input :name "submit" :type "submit" :value "Okay")
  16. (:input ::type "reset" :value "Reset"))))))))))
  17. (publish :path "/simple-form" :function 'simple-form)

Point your browser to http://localhost:2001/simple-form, and you should see a page like the one in Figure 26-5.

If you fill in the form with the “abc” and “def” values, clicking the Okay button should take you to a page like the one in Figure 26-6.

Query Parameters - 图2

Figure 26-5. http://localhost:2001/simple-form

Query Parameters - 图3

Figure 26-6. Result of submitting the simple form

However, most of the time you won’t need to iterate over all the query parameters; you’ll want to pick out individual parameters. For instance, you might want to modify random-number so the limit value you pass to **RANDOM** can be supplied via a query parameter. In that case, you use the function request-query-value, which takes the request object and the name of the parameter whose value you want and returns the value as a string or **NIL** if no such parameter has been supplied. A parameterizable version of random-number might look like this:

  1. (defun random-number (request entity)
  2. (with-http-response (request entity :content-type "text/html")
  3. (with-http-body (request entity)
  4. (let* ((*html-output* (request-reply-stream request))
  5. (limit-string (or (request-query-value "limit" request) ""))
  6. (limit (or (parse-integer limit-string :junk-allowed t) 1000)))
  7. (html
  8. (:html
  9. (:head (:title "Random"))
  10. (:body
  11. (:p "Random number: " (:print (random limit))))))))))

Because request-query-value can return either **NIL** or an empty string, you have to deal with both those cases when parsing the parameter into a number to pass to **RANDOM**. You can deal with a **NIL** value when you bind limit-string, binding it to "" if there’s no “limit” query parameter. Then you can use the :junk-allowed argument to **PARSE-INTEGER** to ensure that it returns either **NIL** (if it can’t parse an integer from the string given) or an integer. In the section “A Small Application Framework,” you’ll develop some macros to make it easier to deal with grabbing query parameters and converting them to various types.