Warning: this post is pretty boring (so unlike my other musings, which are fascinating without exception). I’m putting it up here so that anyone else who runs into this problem will find something about it when they search for ‘VBScript query string parameter order’ (or somesuch).

Last week, I ran into a bug in a simple little ASP/VBScript application I wrote relatively recently. The application does basic routing—URLs take the form http://www.xyz.com/a/b/c, where ‘a/b/c’ means page ‘c’ which is a child of page ‘b’, itself a child of page ‘a’. For extensibility’s sake, there is no particular limit on the depth of the page (i.e., http://www.xyz.com/a/b/c/d/e/f/g would also be valid).

Since we’re an all-M$ shop, we use IIS with ISAPI ReWrite to rewrite our URLs. For this particular application, the URLs are rewritten to http://www.xyz.com/index.asp?a=&b=&c (ad nauseum).

My bug, which was noticed months after the application launched, boiled down to this: when a page was opened as the first page in a browser session—e.g. when someone clicked on a link to the page from an e-mail client, and their default browser was not already open—the application would load the page as if the query string parameters were inverted. So, if the page loaded were http://www.xyz.com/index.asp?a=&b=&c, the application would behave as though the query string were ‘a=&c=&b’. All other load methods resulted in the application behaving as expected (‘a=&b=&c’).

The problem with this was: by design, the application parses the query string left-to-right (i.e. for ‘a=&b=&c’, it checks to see if it can find page ‘a’; then checks for page ‘b’ in pages that are stored as children of page ‘a’, then checks for page ‘c’ in pages that are children of page ‘b’). If at any point the page isn’t found, the application stops, and returns the latest page as it’s best guess. This means that inverted query string parameters are quite a problem—’a=&c=&b’ will check for page ‘a’, find it, then try and look for page ‘c’ in the children of page ‘a’. This will fail, since page ‘c’ is marked only as a child of page ‘b’. Therefore, the application will return page ‘a’, instead of page ‘c’.

To begin getting to the bottom of things, I printed out the value of the current query string key inside the for each loop I was using to iterate. Sure enough, the first time the link was opened, in any browser, the printout read “a, c, b”. After hitting refresh, or loading the page in any way after browser initialization, the printout read “a, b, c”.

Obviously, I was at fault here for assuming that the internal representation of a query string maintained order. What I find odd about it is that the order on initial browser load would differ from the order on any other load.

Apparently, this is just one of the vaguaries of the VBScript Collection object (which is really more or less the same thing as a Scripting.Dictionary object). The fix was to change my method of iterating through the query string—instead of using:

for each item in querystring

I now use:

for i = 1 to querystring.count

All of this just serves to remind me: I really like a language to call a hash a hash. You know—if it is, in fact, a hash.