How to fetch Name/Value of Request.Querystring using For Each Loop but in the same order sent?

2.4k Views Asked by At

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?

2

There are 2 best solutions below

0
Adam On BEST ANSWER

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

%>
3
James Anderbard On

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