In web development, a query string is a crucial URL component that facilitates data exchange between clients and servers. In this article, we will learn how to build a query string for a URL in C#.
Let’s start.
What Is a Query String?
At its core, a query string is a string of characters appended to a URL, typically following a question mark (?
). It consists of key-value pairs separated by ampersands (&
). Each key is associated with a value and ampersands (&
) separate multiple key-value pairs.
Let’s see an example of a query string:
https://test.com/api/Books?author=rowling&language=english
Here, for instance, the URL starts with the base address https://test.com/api/Books
. After the question mark (?
), the query string begins with the first key-value pair: author=rowling
. Here, “author” is the key, and “rowling” is the value. The ampersand (&
) separates the first key-value pair from the second: language=english
.
Different Ways to Build a Query String
There are several ways we can build a query string for a URL:
- String Concatenation
- UriBuilder
- ParseQueryString
- QueryHelpers
- QueryBuilder
- QueryString.Create
Let’s look at each approach to build a query string.
Setting up the Application
To mimic a real API call using the query string, we can set up a simple GET
API that accepts a query string for testing purposes. This API represents a book service that accepts author
and language
as query parameters and returns some book details.
We can construct a query string and then make an API call using it to test if it works. However, setting up the API is optional and not compulsory in this article. Please visit the code for more information about the API.
To begin with, let’s proceed to create a console app and define a BooksApiService
class:
public class BooksApiService { private const string BaseApiUrl = "https://localhost:7220/api/Books"; }
Here, we declare a BaseApiUrl
constant, which holds the base URL for an API endpoint.
Now, let’s explore each section in detail.
Using the String Concatenation Technique
String concatenation is one of the traditional techniques for constructing query strings in C#. This technique combines multiple strings, including parameter names and values, to create a complete query string. While it offers control, it can be cumbersome when dealing with complex queries or numerous parameters.
First, we’ll create a QueryStringHelper
utility class. This class will contain various methods, each employing a different technique to construct query strings.
Now, let’s create our first method to build a query string using string concatenation:
public static class QueryStringHelper { public static string BuildUrlWithQueryStringUsingStringConcat( string basePath, Dictionary<string, string> queryParams) { var queryString = string.Join("&", queryParams.Select(kvp => $"{kvp.Key}={kvp.Value}")); return $"{basePath}?{queryString}"; } }
First, inside the QueryStringHelper
class, we define a method that accepts the basePath
and queryParams
as the input parameters. We then use the Select()
LINQ method to transform the dictionary into a collection of formatted key-value pairs. Then, we use the string.Join()
method to concatenate these pairs with “&” as the separator.
Moving forward, we concatenate the basePath
and the queryString
to form the complete URL and return it.
In this example, one of the primary challenges we face is the URI encoding of the query parameters. If we have an author name with special characters, we might end up with a malformed URL if we don’t encode the special characters when passing the name as a query parameter.
Let’s see an example of how to encode the query parameters:
public static string BuildUrlWithQueryStringUsingStringConcat( string basePath, Dictionary<string, string> queryParams) { var queryString = string.Join("&", queryParams.Select(kvp => $"{HttpUtility.UrlEncode(kvp.Key)}={HttpUtility.UrlEncode(kvp.Value)}")); return $"{basePath}?{queryString}"; }
Here, we use the HttpUtility.UrlEncode()
method to encode both the Key
and Value
of the queryParams
.
Finally, let’s invoke the BuildUrlWithQueryStringUsingStringConcat()
method:
var query = new Dictionary<string, string> { { "author", "George Orwell" }, { "language", "english" } }; Console.WriteLine(QueryStringHelper.BuildUrlWithQueryStringUsingStringConcat(BaseApiUrl, query)); //prints https://localhost:7220/api/Books?author=George+Orwell&language=english
We start by initializing a Dictionary
object, which contains key-value pairs representing the query parameters for the API request. We pass “George Orwell” as the author and “english” as the language.
Finally, we call the BuildUrlWithQueryStringUsingStringConcat()
method, passing the BaseApiUrl
and query
variables to build the complete URL.
It’s important to note that besides string concatenation, there are alternative manual techniques for building query strings in C#. Please visit these articles to learn more: String Interpolation and Different Ways to Concatenate String.
Using the UriBuilder Class
The UriBuilder class in C# provides a powerful and convenient way to construct and modify System.Uri
instances. We can create or manipulate URLs with various components like the scheme, host, port, path, and query string.
The UriBuilder
class handles the URL encoding, ensuring that parameter names and values are correctly encoded for safe URL construction.
Let’s create another method in the QueryStringHelper
class to demonstrate using the UriBuilder
class:
public static string BuildUrlWithQueryStringUsingUriBuilder(string basePath, Dictionary<string, string> queryParams) { var uriBuilder = new UriBuilder(basePath) { Query = string.Join("&", queryParams.Select(kvp => $"{kvp.Key}={kvp.Value}")) }; return uriBuilder.Uri.AbsoluteUri; }
We start by creating an instance of the UriBuilder
class and initialize it with a base API URL. Then, we concatenate the key-value pairs using string.Join()
method and set the Query
property of the uriBuilder
instance. Finally, we obtain the complete API URL by accessing the Uri
property of the UriBuilder
instance and then retrieving its AbsoluteUri
property.
So, let’s proceed to invoke the BuildUrlWithQueryStringUsingUriBuilder()
method:
var query = new Dictionary<string, string> { { "author", "Jane Austen" }, { "language", "english" } }; Console.WriteLine(QueryStringHelper.BuildUrlWithQueryStringUsingUriBuilder(BaseApiUrl, query)); //prints https://localhost:7220/api/Books?author=Jane%20Austen&language=english
In a similar fashion, we initialize a Dictionary
object that holds the query parameters. Then, we pass the BaseApiUrl
and the query
variables to the BuildUrlWithQueryStringUsingUriBuilder()
method to get a complete URL. The UriBuilder
class encodes the author
parameter value that contains a space.
Building a Query String Using HttpUtility.ParseQueryString Method
The HttpUtility.ParseQueryString() method is part of the System.Web
namespace in C#. This method is beneficial when we create or manipulate query strings in web applications.
It allows us to parse an existing query string into a collection of key-value pairs, modify those pairs, and generate a new query string.
We will create a new method within the QueryStringHelper
class to demonstrate this technique:
public static string BuildUrlWithQueryStringUsingParseQueryStringMethod( string basePath, Dictionary<string, string> queryParams) { var query = HttpUtility.ParseQueryString(string.Empty); foreach (var dict in queryParams) { query[dict.Key] = dict.Value; } return string.Join("?", basePath, query.ToString()); }
Here, we create an empty NameValueCollection
using the HttpUtility.ParseQueryString(string.Empty)
method.
This method internally creates an instance of HttpQSCollection
, which is a non-publicly accessible overload of NameValueCollection
. Because it is an internal class, we have to use this non-standard method of initialization. The upside of this special collection is its automatic handling of null
values, along with proper URL encoding of all key-value pairs.
Once we have our query
collection, we add our key-value pairs to it. And finally, we build the complete URL by passing the separator (?
), basePath
, and the query
variable to the string.Join()
method.
If we provide multiple query parameters and one of them has a null
value, it will skip the query parameter with a null
value and print the rest in the query string when we call the ToString()
method. However, if every query parameter passed has a null
value, an ArgumentOutOfRangeException
exception will be thrown. In this case, we assume no null
values will be passed.
Now, let’s call the method:
var query = new Dictionary<string, string> { { "author", "Agatha Christie" }, { "language", "english" } }; Console.WriteLine(QueryStringHelper.BuildUrlWithQueryStringUsingParseQueryStringMethod(BaseApiUrl, query)); //prints https://localhost:7220/api/Books?author=Agatha+Christie&language=english
Here, we create the query parameters using the Dictionary
object and invoke the BuildUrlWithQueryStringUsingParseQueryStringMethod()
method to obtain the complete URL.
Creating a Query String With the QueryHelpers.AddQueryString Method
The QueryHelpers
is a utility class provided by the Microsoft.AspNetCore.WebUtilities
namespace. It includes the AddQueryString()
method that builds the query string by adding or appending parameters to an existing URL.
The QueryHelpers.AddQueryString() method ensures correct URL encoding of parameter names and values for proper URL formation.
As a first step, let’s include the Microsoft.AspNetCore.App
as a FrameworkReference
in the .csproj file within an ItemGroup
:
<FrameworkReference Include="Microsoft.AspNetCore.App" />
Adding this reference ensures that we can use the QueryHelpers.AddQueryString()
method from the Microsoft.AspNetCore.WebUtilities
namespace.
Now, let’s create a new method to demonstrate using the QueryHelpers.AddQueryString()
method:
public static string BuildUrlWithQueryStringUsingAddQueryStringMethod( string basePath, Dictionary<string, string?> queryParams) { return QueryHelpers.AddQueryString(basePath, queryParams); }
Here, we use the QueryHelpers.AddQueryString()
method to generate the query string. We pass the basePath
and the queryParams
dictionary as input, and we get the complete URL.
In this method, we specifically accept the Dictionary<string, string?>
with nullable string values because of the QueryHelpers.AddQueryString()
method supports nullable values, providing flexibility in handling optional query parameters.
Then, let’s invoke the method:
var query = new Dictionary<string, string> { { "author", "Haruki Murakami" }, { "language", "japanese" } }; Console.WriteLine(QueryStringHelper.BuildUrlWithQueryStringUsingAddQueryStringMethod(BaseApiUrl, query)); //prints https://localhost:7220/api/Books?author=Haruki%20Murakami&language=japanese
Similarly, we create a Dictionary
object that holds the query parameters and passes the query
and the BaseApiUrl
variables to the BuildUrlWithQueryStringUsingAddQueryStringMethod()
method to build the complete URL.
Using the QueryBuilder Class
The QueryBuilder class is part of the Microsoft.AspNetCore.Http.Extensions
namespace and is used to construct a query string. In essence, the QueryBuilder
class allows us to build a query string by adding key-value pairs. It implements the IEnumerable<KeyValuePair<String,String>>
interface, which means we can iterate over the key-value pairs contained within it.
Let’s take a look at an example:
public static string BuildUrlWithQueryStringUsingQueryBuilderClass( string basePath, Dictionary<string, string> queryParams) { var queryBuilder = new QueryBuilder(queryParams); return basePath + queryBuilder; }
Here, we create an instance of QueryBuilder
class, and we pass the queryParams
to the constructor, which returns the instance of the QueryBuilder
class.
When we concatenate a queryBuilder
object with a string, it implicitly calls the ToString()
method of the QueryBuilder
class. In the context of the QueryBuilder
class, it overrides the ToString()
method to return the query string representation of the key-value pairs stored in the queryBuilder
object.
Consequently, we can concatenate the queryBuilder
object with the basePath
to create a complete URL.
Let’s proceed and invoke the method:
var query = new Dictionary<string, string> { { "author", "Gabriel Garcia" }, { "language", "spanish" } }; Console.WriteLine(QueryStringHelper.BuildUrlWithQueryStringUsingQueryBuilderClass(BaseApiUrl, query)); //prints https://localhost:7220/api/Books?author=Gabriel%20Garcia&language=spanish
Here, we initialize a Dictionary
object and pass the BuildUrlWithQueryStringUsingQueryBuilderClass()
method to construct the complete URL.
Building a Query String With the QueryString.Create Method
The QueryString.Create() method is a convenient way to create a key-value pair query string object.
The QueryString.Create()
method offers three overloads. The first overload, Create(string name, string value)
, allows us to create a query string with a single key-value pair.
The second overload, Create(IEnumerable<KeyValuePair<string, string?>> parameters)
, accepts a collection of key-value pairs, where values are nullable strings.
Lastly, the third overload, Create(IEnumerable<KeyValuePair<string, StringValues>> parameters)
, takes a collection of key-value pairs where the values are of type StringValues
.
Let’s create a new method in the QueryStringHelper
class:
public static string BuildUrlWithQueryStringUsingCreateMethod( string basePath, Dictionary<string, string?> queryParams) { var queryString = QueryString.Create(queryParams); return basePath + queryString; }
Here, we define a method that accepts a Dictionary<string, string?>
, allowing nullable string values. This choice is made because the second overload of the QueryString.Create()
method supports nullable values.
Although this method doesn’t explicitly accept a Dictionary
, we can still pass a Dictionary
to it. This is possible because Dictionary
implements the IEnumerable<KeyValuePair<string, string>>
interface. Then, we use the QueryString.Create()
method and pass the queryParams
dictionary object to build a query string.
Finally, let’s call the method:
var query = new Dictionary<string, string> { { "author", "Leo Tolstoy" }, { "language", "russian" } }; Console.WriteLine(QueryStringHelper.BuildUrlWithQueryStringUsingCreateMethod(BaseApiUrl, query)); //prints https://localhost:7220/api/Books?author=Leo%20Tolstoy&language=russian
Here, we create a Dictionary
object to represent the query parameters and invoke the BuildUrlWithQueryStringUsingCreateMethod()
method to form the complete URL.
Conclusion
In this article, we have explored various approaches for building a query string in C#. The method we choose will depend on our specific needs and preferences.
To conclude, we can use string concatenation and other manual techniques if we need a straightforward way to build a query string. However, using the string concatenation technique, we must ensure that we properly encode the parameter names and values.
On the other hand, if we need to build complex queries or deal with numerous parameters, we may use a more robust method, such as the UriBuilder
class, the HttpUtility.ParseQueryString(
) method, the QueryHelpers.AddQueryString()
method, the QueryBuilder
class, or the QueryString.Create()
method.
We do not need to manually encode the query string parameters when employing any of these methods. These methods handle URL encoding internally, ensuring secure and accurate URL formation.