[web-devel] Re: Proposed changes to web-routes: query-string support

Jeremy Shaw jeremy at n-heptane.com
Tue Sep 21 16:32:16 EDT 2010

Regarding the query string encoding, this is what I have used in other
code and am likely to use in web-routes, unless it is wrong:

paramsToQueryString :: [(String, String)] -> String
paramsToQueryString [] = ""
paramsToQueryString ps = '?' : concat (intersperse "&" (map
paramToQueryString ps))
      isOK :: Char -> Bool
      isOK c = isUnreserved c || (c `elem` ":@$,")

      escapeParamChar :: Char -> String
      escapeParamChar ' ' = "+"
      escapeParamChar c = escapeURIChar isOK c

      escapeParamString :: String -> String
      escapeParamString = concatMap escapeParamChar

      paramToQueryString :: (String, String) -> String
      paramToQueryString (k,v) = (escapeParamString k) ++ ('=' :
escapeParamString v)

- jeremy

On Tue, Sep 21, 2010 at 2:09 PM, Jeremy Shaw <jeremy at n-heptane.com> wrote:
> Hello,
> There are a few concerns I have with the patch so far:
>  1. When a form uses method="GET", the searchpart (query string) of
> the action is ignored. For example, if you do:
>    <form action="/get?foo=bar" method="GET">...</form>
>  The, foo=bar, will not appear in the query string when the form is
> submitted.  I guess there is really nothing we can do about this
> except document the fact that you shouldn't do that..
>  2. I think the encodeUrlChar function is too aggressive. It seems to
> encode all reserved characters, not just the ones that have special
> meaning?
>  "Reserved characters are those characters that *sometimes* have
> special meaning"
>  "The sets of reserved and unreserved characters and the circumstances
> under which certain reserved characters have special meaning have
> changed slightly with each revision of specifications that govern URIs
> and URI schemes."
>  "When a character from the reserved set (a "reserved character") has
> special meaning (a "reserved purpose") in a certain context, and a URI
> scheme says that it is necessary to use that character for some other
> purpose, then the character must be percent-encoded."
>  I believe that :@$, do not need to be encoded in the query string?
> 3. integration with other parts of web-routes
>  It looks like your patch handles integration with things like RouteT
> by simply hardcoding the query parameters to the empty list. If we are
> going to add support for this, then it would be nice if it was
> actually usable everywhere. I will see what happens when I try to add
> support I guess.
> - jeremy
> On Sun, Aug 8, 2010 at 7:03 AM, Michael Snoyman <michael at snoyman.com> wrote:
>> Hi Jeremy,
>> I've CCed the web-devel list, as I think the discussion will be of interest
>> to a number of readers there.
>> When we were originally working on web-routes, you had mentioned the idea of
>> including some form of support for query strings, and I said it was a bad
>> idea. I'd like to backtrack on that statement: I *would* like to include
>> support, and I'm including a patch that does just that. Let me explain the
>> use case this is meant to serve, which will help explain my design
>> decisions.
>> Yesod includes a static file subsite. One trick a lot of sites are using
>> these days is including a hash of the file contents in the query string of
>> the URL and setting the expiration date of the file far in the future. This
>> way, as long as the file contents stay the same, the browser should use the
>> cached copy, and as soon as a new version is available, the HTML will
>> reflect this by a change in the hash value.
>> I wanted to provide this feature with Yesod, but realized there was no
>> straight-forward way to do this. What I needed was to change the
>> formatPathSegments function to have a type signature of: url -> ([String],
>> [(String, String)]), where the second element in the tuple is the
>> query-string parameters. This, however, presents a small problem: let's say
>> that we want to add query string parameters *on top of* those provided by
>> the formatPathSegments function; we could do manually addition textually,
>> but we won't know whether to include is as "?foo=bar" or "&foo=bar"; the
>> former should be used if formatPathSegments does not given any query string
>> parameter, the latter if it does.
>> I therefore modified the handleSite function to be: (url -> [(String,
>> String)] -> String) -> url -> a. I also changed encodePathInfo to also
>> encode the query string parameters, so its type is now: [String] ->
>> [(String, String)] -> String. All the other changes were just to get the
>> code to compile.
>> I'm planning on this being a change throughout a few other libraries:
>> web-routes-quasi will obviously change to reflect this, as will Yesod. One
>> other perhaps unexpected change would be in Hamlet: the definition of Hamlet
>> is now "type Hamlet = (url -> [(String, String)] -> String) -> Html ()". One
>> nice side effect of this is query-string encoding code is now completely
>> localized to web-routes, instead of copied throughout multiple packages.
>> This at least implies to me that this is a good move.
>> You'll notice that I have purposely *not* modified decodePathInfo and
>> parsePathSegments; I still don't believe routing should be affected in any
>> way by query string parameters.
>> Look forward to hearing your thoughts on this,
>> Michael

More information about the web-devel mailing list