Introduction
In this tip, we will be writing a utility that can read and write URL query strings. It's very basic in functionality but makes reading, editing, writing, and accessing values in a query string very straightforward.
Using this class, you will not have to bother yourself with adding the separating characters between and within parameters. It can also accept a raw query string and parse its segments into objects that contain properties to easily access and set the values.
Using the Code
First, we will create two classes: a QueryBuilder
class, and a QueryParameter
class. The QueryBuilder
class houses a list of QueryParameters
and allows you to access each one by its name or index.
To start, you should add using
statements to your files to import the necessary namespaces which will look as follows:
using System.Collections.Generic;
using System.Linq;
Creating the Classes
Now, create the QueryBuilder
class and the QueryParameter
class and add to the QueryBuilder
class a global variable of type List<QueryParameter>
. Then define a default public
constructor that sets the global variable to a new empty List. This is necessary to prevent errors when adding items to the list.
public class QueryBuilder
{
private List<QueryParameter> _parameters;
public QueryBuilder () { _parameters = new List<QueryParameter>(); }
}
In the QueryParameter
class, add two public
properties, one for the name of the parameter and one for the value. After this, add two constructors to the class, a constructor that accepts a name and value as parameters and one that accepts only a name. The constructor that accepts only a name will set the value to a blank string
.
public class QueryParameter
{
public string Name { get; set; }
public string Value { get; set; }
public QueryParameter (string name, string value)
{
this.Name = name;
this.Value = value;
}
public QueryParameter (string name) : this(name, "") { }
}
Indexers
Once these initial steps are completed, we can add indexers to the QueryBuilder
class to allow access to the contained QueryParameters
.
This indexer returns a QueryParameter
at the specified index.
public QueryParameter this[int index]
{
get { return _parameters[index]; }
}
This indexer returns the Value of a QueryParameter
that has the specified Name
.
public string this[string name]
{
get
{
return HasParameter(name) ? _parameters.Find(p => p.Name == name).Value : null;
}
}
You may notice it contains a method we don't yet have. We need a method like this to avoid Exceptions in the case there is no QueryParameter
with the given name. Let's create it now.
public bool HasParameter (string name) { return _parameters.Exists(p => p.Name == name); }
Adding and Removing QueryParameters
The next thing we need is the ability to add to add and remove QueryParameters
from our QueryBuilder
object. Add the following methods to achieve this:
public void AddParameter (QueryParameter parameter)
{
_parameters.Add(parameter);
}
public void AddParameter (string name, string value)
{
AddParameter(new QueryParameter(name, value));
}
public void RemoveParameter (string name)
{
_parameters.Remove(_parameters.Find(p => p.Name == name));
}
public void RemoveParameter (QueryParameter parameter)
{
_parameters.Remove(parameter);
}
Parsing
We now have functioning objects that can be created, manipulated, and accessed appropriately. However, the ability to create the objects from a passed in string
and the ability to write the objects to a single string
are still missing. A couple more methods should handle that.
QueryParameter
Firstly, we will add a method that will create a QueryParameter
object out of a string
.
public static QueryParameter Create (string parameter)
{
string trimmed = parameter.Replace("&", "").Replace("?", "");
if( trimmed.Contains("=") )
{
string[] split = trimmed.Split('=');
return new QueryParameter(split[0], split[1]);
}
return new QueryParameter(trimmed);
}
In this method, we first remove the unnecessary characters that may have been passed in with our string
. Then we check if an equal sign is inside the remaining text.
If the text does contain an equal sign, we split the parameter string
into its two parts, the name being on the left of the equal sign and the value being on the right. Using our split values, we then pass them into the constructor of a new QueryParameter
object and return it. If the text does not contain an equal sign, we treat it as a name with no value and we use the second constructor we created earlier, then return the object.
The reason we will make the method static
is for ease of use. Its usage would look something like this:
QueryParameter qp = QueryParameter.Create("name=value");
QueryBuilder
Now for reading in an entire query string
. We need to add a method in QueryBuilder
similar to the one we just added to QueryParameter
.
public static QueryBuilder Create (string queryString)
{
QueryBuilder toReturn = new QueryBuilder();
if( queryString.Contains('&') )
{
foreach( string p in queryString.Split('&') )
{
toReturn.AddParameter(QueryParameter.Create(p));
}
}
else
{
toReturn.AddParameter(QueryParameter.Create(queryString));
}
return toReturn;
}
It's static
again for the same reason as before. Let's step through what this code is doing. We first create a QueryBuilder
object that we will be returning once all of the parameters have been parsed and added.
We check if the string
contains an ampersand (&) because its presence would indicate that there is more than one parameter in the string
. If the ampersand is there, we loop through the array created by splitting the string
and create and append each parameter. If there is no ampersand, we treat the string
as a single parameter and add it to the initial QueryBuilder
object, then return it.
Flattening
That takes care of reading from a string
now what about writing to one?
private string Flatten ()
{
return this.Name + ( string.IsNullOrEmpty(this.Value) ? "" : "=" ) + this.Value;
}
This method is one long string
concatenation, starting with the name of the parameter and then we either add an equal sign or nothing depending on if the value is blank. Finally, we add the value to the end, which if it is blank will not change the string
.
We left the method private
because we are going to give public
access to the value by overriding the ToString
method like so:
public override string ToString () { return this.Flatten(); }
This is the method for writing the entire query string
to a string
and it goes in the QueryBuilder
class.
private string CompileQueryString ()
{
return "?" + string.Join("&", _parameters.Select( p => p.ToString() ).ToArray());
}
With each iteration, we append the string
value of the parameter to the returning string
and then return it.
We left this private
for the same reason we left it private
in QueryParameter
and to override ToString
would look something like this:
public override string ToString () { return CompileQueryString(); }
Conclusion
Now, we have a functional and useful way to read and write query string
s. It can be used in the context of an ASP.NET page, a Windows Forms application, or just about any other situation.