Add multi value processing to GetQueryString (#7915)

This commit is contained in:
Cory 2020-04-09 23:18:10 -05:00 committed by GitHub
parent 3010f795fd
commit 937aa6b370
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 157 additions and 6 deletions

View File

@ -138,16 +138,41 @@ namespace Jackett.Common.Utils
return changed ? sb.ToString() : text;
}
public static string GetQueryString(this NameValueCollection collection, Encoding encoding = null) =>
string.Join("&", collection.AllKeys.Select(a =>
$"{a}={WebUtilityHelpers.UrlEncode(collection[a], encoding ?? Encoding.UTF8)}"));
/// <summary>
/// Converts a NameValueCollection to an appropriately formatted query string.
/// Duplicate keys are allowed in a NameValueCollection, but are stored as a csv string in Value.
/// This function handles leaving the values together in the csv string or splitting the value into separate keys
/// </summary>
/// <param name="collection">The NameValueCollection being converted</param>
/// <param name="encoding">The Encoding to use in url encoding Value</param>
/// <param name="duplicateKeysIfMulti">Duplicate keys are handled as true => {"Key=Val1", "Key=Val2} or false => {"Key=Val1,Val2"}</param>
/// <param name="separator">The string used to separate each query value</param>
/// <returns>A web encoded string of key=value parameters separated by the separator</returns>
public static string GetQueryString(this NameValueCollection collection, Encoding encoding = null,
bool duplicateKeysIfMulti = false, string separator = "&") =>
collection.ToEnumerable(duplicateKeysIfMulti).GetQueryString(encoding, separator);
public static string GetQueryString(this ICollection<KeyValuePair<string, string>> collection, Encoding encoding = null) =>
string.Join("&", collection.Select(a =>
$"{a.Key}={WebUtilityHelpers.UrlEncode(a.Value, encoding ?? Encoding.UTF8)}"));
public static string GetQueryString(this IEnumerable<KeyValuePair<string, string>> collection,
Encoding encoding = null, string separator = "&") =>
string.Join(separator,
collection.Select(a => $"{a.Key}={WebUtilityHelpers.UrlEncode(a.Value, encoding ?? Encoding.UTF8)}"));
public static void Add(this ICollection<KeyValuePair<string, string>> collection, string key, string value) => collection.Add(new KeyValuePair<string, string>(key, value));
public static IEnumerable<KeyValuePair<string, string>> ToEnumerable(
this NameValueCollection collection, bool duplicateKeysIfMulti = false)
{
foreach (string key in collection.Keys)
{
var value = collection[key];
if (duplicateKeysIfMulti)
foreach (var val in value.Split(','))
yield return new KeyValuePair<string, string>(key, val);
else
yield return new KeyValuePair<string, string>(key, value);
}
}
public static string ToHtmlPretty(this IElement element)
{
if (element == null)

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using NUnit.Framework;
namespace Jackett.Common.Utils.Tests
{
[TestFixture]
public class StringUtilTests
{
[Test]
public void GetQueryStringTests()
{
#region Encoding Tests
//Add windows-1251 to Encoding list if not present
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var win1251 = Encoding.GetEncoding("windows-1251");
const string encodingTestValue = "Ру́сский";
var encodingNvc = new NameValueCollection
{
{"query", encodingTestValue},
{"space key", "space value"}
};
// WebUtilityHelpers.UrlEncode(encodingTestValue, Encoding.UTF8);
const string utf8Encoded = "%D0%A0%D1%83%CC%81%D1%81%D1%81%D0%BA%D0%B8%D0%B9";
// WebUtilityHelpers.UrlEncode(encodingTestValue, win1251);
const string win1251Encoded = "%D0%F3%3F%F1%F1%EA%E8%E9";
//Default encoding is UTF-8
StringAssert.Contains(utf8Encoded, encodingNvc.GetQueryString());
//Null encoding reverts to default encoding (UTF-8)
StringAssert.Contains(utf8Encoded, encodingNvc.GetQueryString(encoding: null));
//Ensure non-default encoding is utilized
StringAssert.Contains(win1251Encoded, encodingNvc.GetQueryString(encoding: win1251));
//Encoding should make values websafe, but not keys
StringAssert.Contains("space key=space+value", encodingNvc.GetQueryString());
#endregion
#region Separator Tests
var separatorNvc = new NameValueCollection
{
{"one", "value"},
{"two", "value2"}
};
//Ensure default value is "&"
Assert.AreEqual("one=value&two=value2", separatorNvc.GetQueryString());
//Ensure separator is overridden
Assert.AreEqual("one=value;two=value2", separatorNvc.GetQueryString(separator: ";"));
//Ensure behavior when string.IsNullOrEmpty(separator)
const string noSeparator = "one=valuetwo=value2";
Assert.AreEqual(noSeparator, separatorNvc.GetQueryString(separator: null));
Assert.AreEqual(noSeparator, separatorNvc.GetQueryString(separator: string.Empty));
#endregion
#region Split Keys Tests
var duplicateKeysNvc = new NameValueCollection
{
{"key1", "value"},
{"key2", "value2"},
{"key1", "duplicate"}
};
//Default should keep duplicated keys combined
Assert.AreEqual("key1=value%2Cduplicate&key2=value2", duplicateKeysNvc.GetQueryString());
//Ensure keys are combined when requested
Assert.AreEqual(
"key1=value%2Cduplicate&key2=value2", duplicateKeysNvc.GetQueryString(duplicateKeysIfMulti: false));
//Ensure keys are separated when requested
Assert.AreEqual(
"key1=value&key1=duplicate&key2=value2", duplicateKeysNvc.GetQueryString(duplicateKeysIfMulti: true));
#endregion
#region Edge Case Tests
//Throws NullReferenceException if the NameValueCollection is null in all cases
Assert.Throws<NullReferenceException>(() => ((NameValueCollection)null).GetQueryString());
//Returns empty string on empty collection in all cases
Assert.AreEqual(string.Empty, new NameValueCollection().GetQueryString());
#endregion
}
[Test]
public void ToEnumerableTest()
{
var original = new NameValueCollection
{
{"first", "firstVal"},
{"second", "secondVal"},
{"third", "thirdVal"},
{"second", "anotherVal"}
};
var combined = new[]
{
new KeyValuePair<string, string>("first", "firstVal"),
new KeyValuePair<string, string>("second", "secondVal,anotherVal"),
new KeyValuePair<string, string>("third", "thirdVal")
};
var duplicateKeys = new[]
{
new KeyValuePair<string, string>("first", "firstVal"),
new KeyValuePair<string, string>("second", "secondVal"),
new KeyValuePair<string, string>("second", "anotherVal"),
new KeyValuePair<string, string>("third", "thirdVal")
};
CollectionAssert.AreEqual(combined, original.ToEnumerable());
CollectionAssert.AreEqual(duplicateKeys, original.ToEnumerable(true));
}
}
}