HttpUtility.ParseQueryString does not allow to add duplicate key - c#

I have Web API endpoint which expects a list of enum values. I am using HttpUtility.ParseQueryString to generate the query string where I want to pass multiple key-value pair in the query string as below,
../api/getdata?myEnum=One&myEnum=Two&myEnum=Three
But, the ParseQueryString is creating the query string url as below,
../api/getdata?myEnum=One%2cTwo%2cThree
Here is my code,
var queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);
queryString.Add("myEnum", "One");
queryString.Add("myEnum", "Two");
queryString.Add("myEnum", "Three");
How can I create the expected query string url with duplicate keys.
../api/getdata?myEnum=One&myEnum=Two&myEnum=Three

You can achieve the expected result via the AddQueryString method of the QueryHelpers class in ASP.NET Core.
This class is contained in the Microsoft.AspNetCore.WebUtilities NuGet package.
string url = "http://yourservice/api/getdata";
url = QueryHelpers.AddQueryString(url, "myEnum", "One");
url = QueryHelpers.AddQueryString(url, "myEnum", "Two");
url = QueryHelpers.AddQueryString(url, "myEnum", "Three");
Console.WriteLine(url); // http://yourservice/api/getdata?myEnum=One&myEnum=Two&myEnum=Three

This feature has been added a while back [0] using QueryBuilder.
You can now use query builder and pass in an IEnumerable. So if I wanted to add a bunch of id=something parameters to a query I can do this:
var query = new QueryBuilder {{"id", itemIds}}.ToQueryString();
This will give me a query string in the form: ?id=1&id=2&id=...
In your particular case it would be something like this if you had an IEnumerable of your enums.
var query = new QueryBuilder {{"myEnum", myEnums}}.ToQueryString();
[0] https://github.com/dotnet/aspnetcore/issues/7945

Related

C#: How to get a value from a query string containing another url with multiple query params?

When a url containing a query params that has a URL containing query params like this:
https://example.com/login/login.ashx?redirect_url=https://example.com?test=11&test2=22&test3=33
How can I then get https://example.com?test=11&test2=22&test3=33from that Url?
I've tried this with no luck
redirectUrl = HttpUtility.ParseQueryString(HttpUtility.UrlEncode(context.Request.Url.Query)).Get("redirect_url");.
This results in:
https://example.com?test=11
and I want https://example.com?test=11&test2=22&test3=33
Any url parameter that contains url string separators should be UrlEncode'd first.
So you need to System.Net.WebUtility.UrlEncode(string value) your parameter url first.
var urlEncodedParameter = System.Net.WebUtility.UrlEncode("https://example.com?test=11&test2=22&test3=33");
then create url
var url = $"https://example.com/login/login.ashx?redirect_url={urlEncodedParameter}";
Then redirect to that.
And you will not need to parse that parameter on the controller.
It will work on the endpoint with signature like this
public IActionResult Login(string redirect_url)
First of all you need to get full url from browser:
string full_url = HttpContext.Request.GetDisplayUrl().ToString();
Then you need to check that full url for existing "redirect_url" in it and if it exists you can use Substring(int position) method. But before we need to know position of "redirect_url", so we can use LastIndexOf() method:
if (full_url.Contains("redirect_url"))
{
return full_url.Substring(full_url.LastIndexOf("redirect_url"));
}
If there is no "redirect_url" in your full_url we can simply return empty string like this:
return string.Empty;

Building Query String

I have been following this link: Build query string for System.Net.HttpClient get
but my situation was a little different and was not sure what the best approach would be. As of now I am thinking about going with a for loop but figured there might be a better approach.I have a list of parameters. I thought about just for looping over the list but that would make it where the querystring would be "?foo=1?foo=2"
See below example as to what I am looking for and what I have so far
var query = HttpUtility.ParseQueryString(string.Empty);
List<String> foos = ...
query["foo"] = "1";
string queryString = query.ToString()
//Expected query string to be ?foo=1&foo=2
Try Add method on NameViewCollection
With Add method you can add multiple values for a single key.
var qs = HttpUtility.ParseQueryString(string.Empty);
qs.Add("foo", "1");
qs.Add("foo", "2");
qs.ToString(); would give you foo=1&foo=2

How to create keyless URL parameters in ASP.NET

I have seen this answer describing ASP.NET support for keyless (not valueless) parameters, like http://some.url?param1&param2, and confirmed them to be viewable on Request.QueryString like:
var values = this.Request.QueryString.GetValues(null);
values.Any(o => o == "param1");
This is fine and dandy but now I want to generate urls like this. My first intuition was to use the RouteValueDictionary: routeValues parameter of Url.Action with null as a key:
#{
var dict = new RouteValueDictionary();
dict.Add(null, "param1");
dict.Add(null, "param2");
}
Very link, amaze
But apparently C# forbids nulls as dictionary keys because of reasons.
I have also tried the empty string as the key, but it results in a query string like: ?=param1,=param2 which contains 2 more equal signs that I want it to.
Of course I can string manipulate the heck out of my URL and add the &param1 part to the query string, but I was hoping for a concise solution.
You want to add the key values, but leaving the value null isn't allowed.
RouteValueDictionary ignores empty values
You could add a value like 1 for instance, but you lose your fine and dandy solution.
#{
var dict = new RouteValueDictionary();
dict.Add("param1",1);
}
Very link, amaze
For another solution you will have to write some custom code.
Since there's no built-in helper for this why don't you roll your own:
public static class UrlHelperExtensions
{
public static string MyAction(this UrlHelper urlHelper, string actionName, IList<string> parameters)
{
string url = urlHelper.Action(actionName);
if (parameters == null || !parameters.Any())
{
return url;
}
return string.Format("{0}?{1}", url, string.Join("&", parameters));
}
}
and then:
#{
var parameters = new List<string>();
parameters.Add("param1");
parameters.Add("param2");
}
#Url.MyAction("ActionName", parameters)

RedirectToAction with unnamed querystring parameters

In MVC 2 I have a RedirectToAction call which I need to pass all of the querystring parameters. Unfortunately I can only find a way to pass named querystring parameters is there a way of passing all querystring parameters regardless.
We have one named parameter, id but we just want to append all of the rest onto the end of the URL without setting them explicitly.
return RedirectToAction("index", "enquiry", new { id = enquiryCacheId.ToString()});
You cannot pass COMPLEX objects in URLs, so that kills the option of passing on Complex types in new { }.
One option what you are left with is to encode the querystring and then send that in 'id'. Say for examples, you have querystring as follows name=rami&gender=male. Then you can encode it with HttpUtility.UrlEncode(), then set it to id=[encoded string]. On the other action (retrieving side) you can get id value and then use HttpUtility.UrlDecode() to decode the string. Then finally you can use HttpUtility.ParseQueryString() to split the querystring into NameValueCollection.
If above suggestion is not what you are looking for then. you need to add all querystring parameters to new { } in the RedirectToAction(). If you want to customize it then you might need to go to ASP.Net MVC Source code #CodePlex and make your own builds (which I think not worthy for this kind of requirement).
I have an extension method that I use to modify the querystring of the current URL:
public static string ModifyQueryString(this UrlHelper helper, NameValueCollection updates, IEnumerable<string> removes)
{
var request = helper.RequestContext.HttpContext.Request;
var url = request.Url.AbsolutePath;
var query = HttpUtility.ParseQueryString(request.QueryString.ToString());
updates = updates ?? new NameValueCollection();
foreach (string key in updates.Keys)
{
query.Set(key, updates[key]);
}
removes = removes ?? new List<string>();
foreach (string param in removes)
{
query.Remove(param);
}
if (query.HasKeys())
{
return string.Format("{0}?{1}", url, query.ToString());
}
else
{
return url;
}
}
But, if you need to modify an arbitrary URL, it should be easy enough to modify. You would just need to add a parameter to accept an arbitrary URL, and then instead of getting the URL/querystring from the HttpContext, you just split the passed URL at ?. The rest of the code should work the same.

How can I remove item from querystring in asp.net using c#?

I want remove "Language" querystring from my url. How can I do this? (using Asp.net 3.5 , c#)
Default.aspx?Agent=10&Language=2
I want to remove "Language=2", but language would be the first,middle or last. So I will have this
Default.aspx?Agent=20
If it's the HttpRequest.QueryString then you can copy the collection into a writable collection and have your way with it.
NameValueCollection filtered = new NameValueCollection(request.QueryString);
filtered.Remove("Language");
Here is a simple way. Reflector is not needed.
public static string GetQueryStringWithOutParameter(string parameter)
{
var nameValueCollection = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString());
nameValueCollection.Remove(parameter);
string url = HttpContext.Current.Request.Path + "?" + nameValueCollection;
return url;
}
Here QueryString.ToString() is required because Request.QueryString collection is read only.
Finally,
hmemcpy answer was totally for me and thanks to other friends who answered.
I grab the HttpValueCollection using Reflector and wrote the following code
var hebe = new HttpValueCollection();
hebe.Add(HttpUtility.ParseQueryString(Request.Url.Query));
if (!string.IsNullOrEmpty(hebe["Language"]))
hebe.Remove("Language");
Response.Redirect(Request.Url.AbsolutePath + "?" + hebe );
My personal preference here is rewriting the query or working with a namevaluecollection at a lower point, but there are times where the business logic makes neither of those very helpful and sometimes reflection really is what you need. In those circumstances you can just turn off the readonly flag for a moment like so:
// 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);
I answered a similar question a while ago. Basically, the best way would be to use the class HttpValueCollection, which the QueryString property actually is, unfortunately it is internal in the .NET framework.
You could use Reflector to grab it (and place it into your Utils class). This way you could manipulate the query string like a NameValueCollection, but with all the url encoding/decoding issues taken care for you.
HttpValueCollection extends NameValueCollection, and has a constructor that takes an encoded query string (ampersands and question marks included), and it overrides a ToString() method to later rebuild the query string from the underlying collection.
Try this ...
PropertyInfo isreadonly =typeof(System.Collections.Specialized.NameValueCollection).GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
isreadonly.SetValue(this.Request.QueryString, false, null);
this.Request.QueryString.Remove("foo");
Gather your query string by using HttpContext.Request.QueryString. It defaults as a NameValueCollection type.
Cast it as a string and use System.Web.HttpUtility.ParseQueryString() to parse the query string (which returns a NameValueCollection again).
You can then use the Remove() function to remove the specific parameter (using the key to reference that parameter to remove).
Use case the query parameters back to a string and use string.Join() to format the query string as something readable by your URL as valid query parameters.
See below for a working example, where param_to_remove is the parameter you want to remove.
Let's say your query parameters are param1=1&param_to_remove=stuff&param2=2. Run the following lines:
var queryParams = System.Web.HttpUtility.ParseQueryString(HttpContext.Request.QueryString.ToString());
queryParams.Remove("param_to_remove");
string queryString = string.Join("&", queryParams.Cast<string>().Select(e => e + "=" + queryParams[e]));
Now your query string should be param1=1&param2=2.
You don't make it clear whether you're trying to modify the Querystring in place in the Request object. Since that property is read-only, I guess we'll assume you just want to mess with the string.
... In which case, it's borderline trivial.
grab the querystring off the Request
.split() it on '&'
put it back together into a new string, while sniffing for and tossing out anything starting with "language"
Get the querystring collection, parse it into a (name=value pair) string, excluding the one you want to REMOVE, and name it newQueryString
Then call Response.Redirect(known_path?newqueryString);
You're probably going to want use a Regular Expression to find the parameter you want to remove from the querystring, then remove it and redirect the browser to the same file with your new querystring.
Yes, there are no classes built into .NET to edit query strings. You'll have to either use Regex or some other method of altering the string itself.
If you have already the Query String as a string, you can also use simple string manipulation:
int pos = queryString.ToLower().IndexOf("parameter=");
if (pos >= 0)
{
int pos_end = queryString.IndexOf("&", pos);
if (pos_end >= 0) // there are additional parameters after this one
queryString = queryString.Substring(0, pos) + queryString.Substring(pos_end + 1);
else
if (pos == 0) // this one is the only parameter
queryString = "";
else // this one is the last parameter
queryString=queryString.Substring(0, pos - 1);
}
well I have a simple solution , but there is a little javascript involve.
assuming the Query String is "ok=1"
string url = Request.Url.AbsoluteUri.Replace("&ok=1", "");
url = Request.Url.AbsoluteUri.Replace("?ok=1", "");
Response.Write("<script>window.location = '"+url+"';</script>");
string queryString = "Default.aspx?Agent=10&Language=2"; //Request.QueryString.ToString();
string parameterToRemove="Language"; //parameter which we want to remove
string regex=string.Format("(&{0}=[^&\s]+|{0}=[^&\s]+&?)",parameterToRemove);
string finalQS = Regex.Replace(queryString, regex, "");
https://regexr.com/3i9vj
Parse Querystring into a NameValueCollection. Remove an item. And use the toString to convert it back to a querystring.
using System.Collections.Specialized;
NameValueCollection filteredQueryString = System.Web.HttpUtility.ParseQueryString(Request.QueryString.ToString());
filteredQueryString.Remove("appKey");
var queryString = '?'+ filteredQueryString.ToString();
ASP .NET Core (native, don't have to reference any additional libraries)
Within an ASP .NET Core Controller you would have access to an instance of Request
Request.Query is a query collection representing the query parameters, cast it to a list
From which you can filter and remove the params you want
Use QueryString.Create, which can take the list you just filtered as an input & generate a query string directly
var removeTheseParams = new List<string> {"removeMe1", "removeMe2"}.AsReadOnly();
var filteredQueryParams = Request.Query.ToList().Where(filterKvp => !removeTheseParams.Contains(filterKvp.Key));
var filteredQueryString = QueryString.Create(queryParamsFilteredList).ToString();
//Example: Console.Writeline(filteredQueryString) will give you "?q1=v1&q2=v2"
Optional Part Below: Can also encode those values if they are unsafe,
so in addition to the Where() above UrlEncode the query parameter keys and values using a Select() as shown below:
//Optional
.Select(cleanKvp => new KeyValuePair<string, string?>(UrlEncoder.Default.Encode(cleanKvp.Key),UrlEncoder.Default.Encode(cleanKvp.Value)))

Categories

Resources