0

I was trying to fetch the Name/Value of a querystring using For Each loop, as per @kloarubeek answer shown in → Request.Querystring(variablevalue) possible? as follows:

For Each Item in Request.QueryString
Response.Write Item & ": " & Request.QueryString(Item) & "<br/>"
Next

But it returns the items not in the order sent, for example:
if my queryString is:

"Item1=1&Item2=2&Item3=3&Item4=4"

then the For Each Loop returns:

Item1=1
Item3=3
Item2=2
Item4=4

I managed to work around it by splitting it into an Array and then loop inside this array and re-split each item again into name and value. but it's a long and not very efficient coding.

So is there a better and shorter way to fetch the querystring's name/value using the For Each Loop but in the same order?

Code Reaper
  • 143
  • 1
  • 9
  • Why is the order important? – user692942 Mar 17 '19 at 06:08
  • I need it in the same order as I switch between two languages and I'm using the url rewrite module for SEO URLs that url becomes missed up if it doesn't match the rule pattern. like for example the "Item1=1&Item2=2&Item3=3&Item4=4" becomes "1/2/3/4" in the url. so I need to take it dynamically one by one and convert the value to the corresponded language but keep the original order as is. – Code Reaper Mar 17 '19 at 08:00

2 Answers2

1

The QueryString object is a NameValueCollection, which according to MSDN:

Collections of this type do not preserve the ordering of elements, and no particular ordering is guaranteed when enumerating the collection.

So it doesn't support getting it in the same order as the url.

Edited to add an option:

That being said, it looks like the only way will be to parse the url. In this stackoverflow question (Get individual query parameters from Uri) one of the answers includes a section on parsing it using regex (all other answers there lead to the same NameValueCollection). You will want to modify it slightly as Dictionary class has the same issue of not keeping the order. I would change it to be this:

public static class UriExtensions
{
    private static readonly Regex _regex = new Regex(@"[?&](\w[\w.]*)=([^?&]+)");

    public static List<KeyValuePair<string, string>> ParseQueryString(this Uri uri)
    {
        var match = _regex.Match(uri.PathAndQuery);
        var paramaters = new List<KeyValuePair<string, string>>();
        while (match.Success)
        {
            paramaters.Add(new KeyValuePair(match.Groups[1].Value, match.Groups[2].Value));
            match = match.NextMatch();
        }
        return paramaters;
    }
}

To call it you'd do this:

Request.Url.ParseQueryString()

I will note I have not tested this method myself and am going off of the fact that it was bumped up on the other question as a good answer.

https://learn.microsoft.com/en-us/dotnet/api/system.web.httprequest.querystring?view=netframework-4.7.2 https://learn.microsoft.com/en-us/dotnet/api/system.collections.specialized.namevaluecollection?view=netframework-4.7.2

James Anderbard
  • 374
  • 2
  • 6
  • 20
  • Yes, I saw what you referred to in the documentation but the thing is it is unordered only when using the for each loop but if I target the querystring with the index number it gets the order right! but as I mentioned it a longer and not so clean process. – Code Reaper Mar 17 '19 at 03:57
  • 1
    What does any of this have to do with [tag:asp-classic]? Your answer is about [tag:asp.net] which is completely different to [tag:asp-classic]. – user692942 Mar 17 '19 at 05:48
  • @CodeReaper the documentation they refer to is for ASP.Net which isn't Classic ASP. – user692942 Mar 17 '19 at 06:09
1

Your for each statement should loop through the query strings in the order they were passed, it's very strange that it doesn't, I'd never heard of that before. But as your parameters are numbered you can assign them to an array and reorder based on their numerical indicators.

This is how I would tackle this issue, I don't know if it's shorter than your method of splitting and reordering, but it does seem to be efficient:

<%

    if request.querystring.count > 0 then

        Dim lookFor, queryStrings(), items(), x, y

        ' Specify the query string name to find and reorder. The name should have a numeric suffix.

        lookFor = "item" ' Looking for item1, item2, item3, item4 etc...

        ' Create a 2d array, set the upperbound value to match the number of query strings passed.

        ReDim queryStrings(request.querystring.count-1,1)

        ' Loop through each query string.

        x = 0 : for each item in request.querystring

            ' We're only interested in query strings where the name starts with "item". Use inStr()
            ' with a textual comparison to check, and use replace() to check that the item name has 
            ' a numeric suffix.

            if inStr(1,item,lookFor,1) = 1 AND isNumeric(replace(item,lookFor,"",1,-1,1)) then

                ' Only store the number part of the query string name. This is needed for reordering later.

                queryStrings(x,0) = int(replace(item,lookFor,"",1,-1,1))
                queryStrings(x,1) = request.querystring(item)
                x = x + 1

            end if

        Next

        ' The queryStrings array may contain empty values. We can't use "ReDim Preserve" to resize
        ' a 2d array, so instead let's create another 2d array and set the upperbound value to the
        ' number of items found.

        ReDim items(x-1,1)

        ' Assign the non-empty data from the queryStrings array to the items array.

        y = 0 : for x = 0 to uBound(queryStrings)
            if NOT isEmpty(queryStrings(x,0)) then
                items(y,0) = queryStrings(x,0)
                items(y,1) = queryStrings(x,1)
                y = y + 1
            end if
        next

        ' Reorder the items array.

        Dim temp0, temp1

        for x = uBound(items)-1 to 0 step-1
            for y = 0 to x
                if items(y,0) > items(y+1,0) then
                    temp0 = items(y+1,0)
                    temp1 = items(y+1,1)
                    items(y+1,0) = items(y,0)
                    items(y+1,1) = items(y,1)               
                    items(y,0) = temp0
                    items(y,1) = temp1
                end if
            next
        next

        ' Finally, output the reordered items array.

        for x = 0 to uBound(items)  
            response.write lookFor & items(x,0) & ": " & items(x,1) & "<br>"
        next

    end if

%>


Let's take a completely jumbled up query string order:

?Item3=3
&Item6=6
&Item2=2
&Item7=7
&Item4=4
&Item1=1
&Item5=5

Output:

item1: 1
item2: 2
item3: 3
item4: 4
item5: 5
item6: 6
item7: 7


Let's do the same, but throw in some additional parameters:

?foo1=bar1
&Item2=2
&foo2=bar2
&Item5=5
&Item1=1
&Item3=3
&Item4=4
&foo3=bar3

Output:

item1: 1
item2: 2
item3: 3
item4: 4
item5: 5

The additional parameters are ignored


Finally, let's pass some duplicate parameters:

?Item2=2
&Item4=Four
&Item5=5
&Item1=1
&Item2=Two
&Item3=3
&Item4=4

Output:

item1: 1
item2: 2, Two
item3: 3
item4: Four, 4
item5: 5


EDIT: I assumed the fault was with IIS or ASP reordering the querystring header (somehow?), but after seeing your comment where you said if I target the querystring with the index number it gets the order right the headers are obviously correct and the fault lies within the for each method (as outlined in the other answer). A much simpler solution would be to split the raw querystring header yourself (rather than using for each) and loop through looking for the items:

<%

    Dim splitQS, reSplitQS, x, lookFor : lookFor = "item"

    splitQS = split(Request.QueryString,"&")

    for x = 0 to uBound(splitQS)

        reSplitQS = split(splitQS(x),"=")

        if uBound(reSplitQS) = 1 then

            if inStr(1,reSplitQS(0),lookFor,1) = 1 then

                response.write reSplitQS(0) & ": " &_
                request.QueryString(reSplitQS(0)) & "<br>"

            end if

        end if

    next

%>
Adam
  • 836
  • 2
  • 8
  • 13