A common task when calling web resources from a code is building a query string to including all the necessary parameters. While by all means no rocket science, there are so
I wrote some extension methods that I have found very useful when working with QueryStrings. Often I want to start with the current QueryString and modify before using it. Something like,
var res = Request.QueryString.Duplicate()
.ChangeField("field1", "somevalue")
.ChangeField("field2", "only if following is true", true)
.ChangeField("id", id, id>0)
.WriteLocalPathWithQuery(Request.Url)); //Uses context to write the path
For more and the source: http://www.charlesrcook.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx
It's basic, but I like the style.
A quick extension method based version:
class Program
{
static void Main(string[] args)
{
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("A", "AValue"),
new KeyValuePair<string, string>("B", "BValue")
};
string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray());
}
}
public static class KeyValueExtensions
{
public static string ToQueryString(this KeyValuePair<string, string> obj)
{
return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value);
}
}
You could use a where clause to select which parameters get added to the string.
The query string can be added to a URL by:
https://blog.codingnovice.com/blog
public ActionResult Create()
{
//declaring name value collection object
NameValueCollection collection = new NameValueCollection();
//adding new value to the name value collection object
collection.Add("Id1", "wwe323");
collection.Add("Id2", "454w");
collection.Add("Id3", "tyt5656");
collection.Add("Id4", "343wdsd");
//generating query string
string url = GenerateQueryString(collection);
return View();
}
private string GenerateQueryString(NameValueCollection collection)
{
var querystring = (
from key in collection.AllKeys
from value in collection.GetValues(key)
select string.Format("{0}={1}",
HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(value))
).ToArray();
return "?" + string.Join("&", querystring);
}
In dotnet core QueryHelpers.AddQueryString() will accept an IDictionary of key-value pairs. To save a few memory allocs and CPU cycles you can use SortedList<,> instead of IDictionary<,>, with an appropriate capacity and items added in sort order...
var queryParams = new SortedList<string,string>(2);
queryParams.Add("abc", "val1");
queryParams.Add("def", "val2");
string requestUri = QueryHelpers.AddQueryString("https://localhost/api", queryParams);
// USAGE
[TestMethod]
public void TestUrlBuilder()
{
Console.WriteLine(
new UrlBuilder("http://www.google.com?A=B")
.AddPath("SomePathName")
.AddPath("AnotherPathName")
.SetQuery("SomeQueryKey", "SomeQueryValue")
.AlterQuery("A", x => x + "C"));
}
Output:
http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue
The code; you can all thank me somewhere, somehow :D
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
// By Demetris Leptos
namespace TheOperator.Foundation.Web
{
public class UrlBuilder
{
public string Scheme { get; set; }
public string Host { get; set; }
public int? Port { get; set; }
public List<string> Paths { get; set; }
public SortedDictionary<string, string> QueryPairs { get; set; }
public UrlBuilder(string url)
{
this.Paths = new List<string>();
this.QueryPairs = new SortedDictionary<string, string>();
string path = null;
string query = null;
Uri relativeUri = null;
if (!Uri.TryCreate(url, UriKind.Relative, out relativeUri))
{
var uriBuilder = new UriBuilder(url);
this.Scheme = uriBuilder.Scheme;
this.Host = uriBuilder.Host;
this.Port = uriBuilder.Port;
path = uriBuilder.Path;
query = uriBuilder.Query;
}
else
{
var queryIndex = url.IndexOf('?');
if (queryIndex >= 0)
{
path = url.Substring(0, queryIndex);
query = url.Substring(queryIndex + 1);
}
else
{
path = url;
}
}
this.Paths.AddRange(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));
if (query != null)
{
var queryKeyValuePairs = HttpUtility.ParseQueryString(query);
foreach (var queryKey in queryKeyValuePairs.AllKeys)
{
this.QueryPairs[queryKey] = queryKeyValuePairs[queryKey];
}
}
}
public UrlBuilder AddPath(string value)
{
this.Paths.Add(value);
return this;
}
public UrlBuilder SetQuery(string key, string value)
{
this.QueryPairs[key] = value;
return this;
}
public UrlBuilder RemoveQuery(string key)
{
this.QueryPairs.Remove(key);
return this;
}
public UrlBuilder AlterQuery(string key, Func<string, string> alterMethod, bool removeOnNull = false)
{
string value;
this.QueryPairs.TryGetValue(key, out value);
value = alterMethod(value);
if (removeOnNull && value == null)
{
return this.RemoveQuery(key);
}
else
{
return this.SetQuery(key, value);
}
}
public override string ToString()
{
var path = !string.IsNullOrWhiteSpace(this.Host)
? string.Join("/", this.Host, string.Join("/", this.Paths))
: string.Join("/", this.Paths);
var query = string.Join("&", this.QueryPairs.Select(x => string.Concat(x.Key, "=", HttpUtility.UrlEncode(x.Value))));
return string.Concat(
!string.IsNullOrWhiteSpace(this.Scheme) ? string.Concat(this.Scheme, "://") : null,
path,
!string.IsNullOrWhiteSpace(query) ? string.Concat("?", query) : null);
}
}
}
If you look under the hood the QueryString property is a NameValueCollection. When I've done similar things I've usually been interested in serialising AND deserialising so my suggestion is to build a NameValueCollection up and then pass to:
using System.Linq;
using System.Web;
using System.Collections.Specialized;
private string ToQueryString(NameValueCollection nvc)
{
var array = (
from key in nvc.AllKeys
from value in nvc.GetValues(key)
select string.Format(
"{0}={1}",
HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(value))
).ToArray();
return "?" + string.Join("&", array);
}
I imagine there's a super elegant way to do this in LINQ too...