25

I have an ASP.NET page which takes a number of parameters in the query string:

search.aspx?q=123&source=WebSearch

This would display the first page of search results. Now within the rendering of that page, I want to display a set of links that allow the user to jump to different pages within the search results. I can do this simply by append &page=1 or &page=2 etc.

Where it gets complicated is that I want to preserve the input query string from the original page for every parameter except the one that I'm trying to change. There may be other parameters in the url used by other components and the value I'm trying to replace may or may not already be defined:

search.aspx?q=123&source=WebSearch&page=1&Theme=Blue

In this case to generate a link to the next page of results, I want to change page=1 to page=2 while leaving the rest of the query string unchanged.

Is there a builtin way to do this, or do I need to do all of the string parsing/recombining manually?

star
  • 251
  • 1
  • 3
  • 4

5 Answers5

57

You can't modify the QueryString directly as it is readonly. You will need to get the values, modify them, then put them back together. Try this:

var nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
nameValues.Set("page", "2");
string url = Request.Url.AbsolutePath;
string updatedQueryString = "?" + nameValues.ToString();
Response.Redirect(url + updatedQueryString);

The ParseQueryString method returns a NameValueCollection (actually it really returns a HttpValueCollection which encodes the results, as I mention in an answer to another question). You can then use the Set method to update a value. You can also use the Add method to add a new one, or Remove to remove a value. Finally, calling ToString() on the name NameValueCollection returns the name value pairs in a name1=value1&name2=value2 querystring ready format. Once you have that append it to the URL and redirect.

Alternately, you can add a new key, or modify an existing one, using the indexer:

nameValues["temp"] = "hello!"; // add "temp" if it didn't exist
nameValues["temp"] = "hello, world!"; // overwrite "temp"
nameValues.Remove("temp"); // can't remove via indexer

You may need to add a using System.Collections.Specialized; to make use of the NameValueCollection class.

Community
  • 1
  • 1
Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
  • Actually you can set it directly, reflection lets you modify this. I personally agree with using an NVC though. – annakata Sep 28 '10 at 15:25
  • @annakata just read your response. Interesting approach, not the first thing that would come to mind. – Ahmad Mageed Sep 28 '10 at 15:29
  • There is one more problem with ToString method. It incorrectly handles keys with multiple values. Instead of making comma separated list, it duplicate pairs key=value. For example if you have added entry param two times with values value1, value2 you will get result param=value1&param=value2 instead of param=value1,value2. – Stoune Nov 20 '15 at 12:26
14

You can do this without all the overhead of redirection (which is not inconsiderable). My personal preference is to work with a NameValueCollection which a querystring really is, but using reflection:

// reflect to readonly property 
PropertyInfo isReadOnly = typeof(System.Collections.Specialized.NameValueCollection).GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic); 

// make collection editable 
isReadOnly.SetValue(this.Request.QueryString, false, null); 

// remove 
this.Request.QueryString.Remove("foo"); 

// modify 
this.Request.QueryString.Set("bar", "123"); 

// make collection readonly again 
isReadOnly.SetValue(this.Request.QueryString, true, null); 
annakata
  • 74,572
  • 17
  • 113
  • 180
  • This is a pretty nice solution that I wasn't sure if it would work but did the trick for me. I couldn't really afford a response.redirect based on my architecture and could avoid with this approach. Thanks! – Jeff Fol Dec 12 '12 at 00:21
  • I'm getting the error "'PropertyInfo' is a type in 'Reflection' and cannot be used as an expression." – jbyrd Oct 12 '16 at 13:59
5

Using this QueryStringBuilder helper class, you can grab the current QueryString and call the Add method to change an existing key/value pair...

//before: "?id=123&page=1&sessionId=ABC"
string newQueryString = QueryString.Current.Add("page", "2");
//after: "?id=123&page=2&sessionId=ABC"
Josh Stodola
  • 81,538
  • 47
  • 180
  • 227
0

Use the URIBuilder Specifically the link textQuery property

I believe that does what you need.

Chris Chadwick
  • 607
  • 1
  • 7
  • 13
  • UriBuilder lets me change out the entire query component of the request url, what I'm trying to do is to change/add one specific query parameter within the url. – star Sep 28 '10 at 14:58
0

This is pretty arbitrary, in .NET Core at least. And it all boils down to asp-all-route-data

Consider the following trivial example (taken from the "paginator" view model I use in virtually every project):

public class SomeViewModel 
{
    
    public Dictionary<string, string> NextPageLink(IQueryCollection query)
    {
        /*
         * NOTE: how you derive the "2" is fully up to you
         */
        return ParseQueryCollection(query, "page", "2");
    }

    Dictionary<string, string> ParseQueryCollection(IQueryCollection query, string replacementKey, string replacementValue)
    {
        var dict = new Dictionary<string, string>()
        {
            { replacementKey, replacementValue }
        };

        foreach (var q in query)
        {
            if (!string.Equals(q.Key, replacementKey, StringComparison.OrdinalIgnoreCase))
            {
                dict.Add(q.Key, q.Value);
            }
        }

        return dict;
    }
}

Then to use in your view, simply pass the method the current request query collection from Context.Request:

<a asp-all-route-data="@Model.NextPageLink(Context.Request.Query)">Next</a>
pim
  • 12,019
  • 6
  • 66
  • 69