ASP.NET

Passing multiple parameters in an ASP.NET QueryString (C#)

ASP.NET · · Published 2026-10-19

A query string is the part of a URL after the question mark: products.aspx?category=books&page=2. It is the simplest way to pass a few values from one page to another, and it makes the result bookmarkable. Getting it right means building it with encoding, reading it defensively and remembering that the user can change every value.

Anatomy

/products.aspx?category=books&sort=price&page=2
               └─────┬──────┘ └───┬────┘ └──┬─┘
                 name=value   name=value  name=value   (pairs separated by &)

Names and values are percent-encoded: a space becomes %20 (or + in form encoding), & becomes %26, and so on. In HTML, an & inside an href attribute should itself be written &; ASP.NET controls such as HyperLink do that for you.

Sending several parameters

Encoding each value

using System.Web;

string category = txtCategory.Text;     // e.g. "Books & magazines"
string sort = ddlSort.SelectedValue;    // e.g. "price"
int page = 2;

string url = "Products.aspx?category=" + HttpUtility.UrlEncode(category)
           + "&sort=" + HttpUtility.UrlEncode(sort)
           + "&page=" + page;

Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();

Without UrlEncode, "Books & magazines" would end the category value at the ampersand and create a stray parameter named " magazines". The article on passing an ampersand in a query string looks at that case in detail.

Response.Redirect(url, false) followed by CompleteRequest() avoids the ThreadAbortException that Response.Redirect(url) raises to stop the page. Either form sends the browser a 302 response pointing to the new URL.

Building the query with ParseQueryString

For more than two or three values, let the framework assemble and encode the string:

var query = HttpUtility.ParseQueryString(string.Empty); // an HttpValueCollection
query["category"] = category;
query["sort"] = sort;
query["page"] = page.ToString();

string url = "Products.aspx?" + query.ToString(); // encoded: category=Books+%26+magazines&sort=price&page=2

The collection returned by ParseQueryString overrides ToString() to produce an encoded query string, which a plain NameValueCollection does not.

From markup

<asp:HyperLink runat="server"
    NavigateUrl='<%# "Product.aspx?id=" + Eval("ProductId") + "&ref=list" %>'
    Text='<%#: Eval("Name") %>' />

Numeric ids need no encoding. For text, wrap the value: HttpUtility.UrlEncode(Eval("Name").ToString()).

Reading the parameters

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack) return;

    // Missing keys return null, not an exception.
    string category = Request.QueryString["category"] ?? "";
    string sort = Request.QueryString["sort"];

    // Allow-list the values you expect
    if (sort != "price" && sort != "name" && sort != "date") sort = "name";

    // Parse numbers defensively
    if (!int.TryParse(Request.QueryString["page"], out int page) || page < 1 || page > 1000)
        page = 1;

    lblInfo.Text = "Category: " + Server.HtmlEncode(category) + ", sort: " + sort + ", page " + page;
    LoadProducts(category, sort, page);
}

ASP.NET has already decoded the values: Request.QueryString["category"] returns "Books & magazines", not the encoded form. Do not decode a second time, or a value containing a literal % or + will be corrupted.

Everything in the URL is untrusted input. Use parameters when the values reach SQL (see SQL injection), encode them when you write them into HTML, and check that the current user may see the record an id points to.

Repeated keys and listing everything

A URL such as ?tag=sql&tag=jquery carries two values for one key. The indexer joins them with commas, which is ambiguous if values may contain commas; GetValues returns them separately:

string joined = Request.QueryString["tag"];          // "sql,jquery"
string[] tags = Request.QueryString.GetValues("tag"); // { "sql", "jquery" } or null

foreach (string key in Request.QueryString.AllKeys)
{
    // key can be null for a value without a name, e.g. "?flag"
    System.Diagnostics.Debug.WriteLine(key + " = " + Request.QueryString[key]);
}

A two-page example

Search.aspx collects the criteria:

<asp:TextBox ID="txtCity" runat="server" />
<asp:DropDownList ID="ddlType" runat="server">
    <asp:ListItem Value="hotel">Hotel</asp:ListItem>
    <asp:ListItem Value="apartment">Apartment</asp:ListItem>
</asp:DropDownList>
<asp:TextBox ID="txtGuests" runat="server" TextMode="Number" Text="2" />
<asp:Button ID="btnSearch" runat="server" Text="Search" OnClick="btnSearch_Click" />
protected void btnSearch_Click(object sender, EventArgs e)
{
    var q = HttpUtility.ParseQueryString(string.Empty);
    q["city"] = txtCity.Text.Trim();
    q["type"] = ddlType.SelectedValue;
    q["guests"] = txtGuests.Text.Trim();
    Response.Redirect("Results.aspx?" + q, false);
    Context.ApplicationInstance.CompleteRequest();
}

Results.aspx reads them:

protected void Page_Load(object sender, EventArgs e)
{
    string city = (Request.QueryString["city"] ?? "").Trim();
    string type = Request.QueryString["type"] == "apartment" ? "apartment" : "hotel";
    int guests = int.TryParse(Request.QueryString["guests"], out int g) && g is >= 1 and <= 12 ? g : 2;

    litSummary.Text = Server.HtmlEncode($"{type}s in {city} for {guests} guests");
}

The pattern check g is >= 1 and <= 12 needs C# 9. On an older compiler write g >= 1 && g <= 12.

Reading the query string in the browser

Client-side code can read the same values with URLSearchParams: new URLSearchParams(location.search).get("city"). The jQuery-era approach is in Get a QueryString value on the client.

When not to use the query string

For passing values between pages without the URL, see cross-page posting and session state.

Debugging query strings

When a value seems lost, look at the actual URL in the browser's address bar or in the developer tools' network tab before changing code. A value cut at an ampersand or a # means encoding is missing on the sending side; a value with %2526 means it was encoded twice. On the receiving side, Request.QueryString.ToString() shows the raw query as ASP.NET received it, and Request.RawUrl the full path and query. Log these during development rather than guessing.

Official documentation