Las respuestas proporcionadas tienen problemas con las URL relativas, como "/ some / path /". Esto es una limitación de la clase Uri y UriBuilder, que es bastante difícil de entender, ya que no veo ninguna razón por la cual las URL relativas sean problemáticas. cuando se trata de manipulación de consultas.
Aquí hay una solución alternativa que funciona tanto para rutas absolutas como relativas, escritas y probadas en .NET 4:
(pequeña nota: esto también debería funcionar en .NET 4.5, solo tendrá que cambiar propInfo.GetValue(values, null)
a propInfo.GetValue(values)
)
public static class UriExtensions{
/// <summary>
/// Adds query string value to an existing url, both absolute and relative URI's are supported.
/// </summary>
/// <example>
/// <code>
/// // returns "www.domain.com/test?param1=val1&param2=val2&param3=val3"
/// new Uri("www.domain.com/test?param1=val1").ExtendQuery(new Dictionary<string, string> { { "param2", "val2" }, { "param3", "val3" } });
///
/// // returns "/test?param1=val1&param2=val2&param3=val3"
/// new Uri("/test?param1=val1").ExtendQuery(new Dictionary<string, string> { { "param2", "val2" }, { "param3", "val3" } });
/// </code>
/// </example>
/// <param name="uri"></param>
/// <param name="values"></param>
/// <returns></returns>
public static Uri ExtendQuery(this Uri uri, IDictionary<string, string> values) {
var baseUrl = uri.ToString();
var queryString = string.Empty;
if (baseUrl.Contains("?")) {
var urlSplit = baseUrl.Split('?');
baseUrl = urlSplit[0];
queryString = urlSplit.Length > 1 ? urlSplit[1] : string.Empty;
}
NameValueCollection queryCollection = HttpUtility.ParseQueryString(queryString);
foreach (var kvp in values ?? new Dictionary<string, string>()) {
queryCollection[kvp.Key] = kvp.Value;
}
var uriKind = uri.IsAbsoluteUri ? UriKind.Absolute : UriKind.Relative;
return queryCollection.Count == 0
? new Uri(baseUrl, uriKind)
: new Uri(string.Format("{0}?{1}", baseUrl, queryCollection), uriKind);
}
/// <summary>
/// Adds query string value to an existing url, both absolute and relative URI's are supported.
/// </summary>
/// <example>
/// <code>
/// // returns "www.domain.com/test?param1=val1&param2=val2&param3=val3"
/// new Uri("www.domain.com/test?param1=val1").ExtendQuery(new { param2 = "val2", param3 = "val3" });
///
/// // returns "/test?param1=val1&param2=val2&param3=val3"
/// new Uri("/test?param1=val1").ExtendQuery(new { param2 = "val2", param3 = "val3" });
/// </code>
/// </example>
/// <param name="uri"></param>
/// <param name="values"></param>
/// <returns></returns>
public static Uri ExtendQuery(this Uri uri, object values) {
return ExtendQuery(uri, values.GetType().GetProperties().ToDictionary
(
propInfo => propInfo.Name,
propInfo => { var value = propInfo.GetValue(values, null); return value != null ? value.ToString() : null; }
));
}
}
Y aquí hay un conjunto de pruebas unitarias para probar el comportamiento:
[TestFixture]
public class UriExtensionsTests {
[Test]
public void Add_to_query_string_dictionary_when_url_contains_no_query_string_and_values_is_empty_should_return_url_without_changing_it() {
Uri url = new Uri("http://www.domain.com/test");
var values = new Dictionary<string, string>();
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test")));
}
[Test]
public void Add_to_query_string_dictionary_when_url_contains_hash_and_query_string_values_are_empty_should_return_url_without_changing_it() {
Uri url = new Uri("http://www.domain.com/test#div");
var values = new Dictionary<string, string>();
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test#div")));
}
[Test]
public void Add_to_query_string_dictionary_when_url_contains_no_query_string_should_add_values() {
Uri url = new Uri("http://www.domain.com/test");
var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1¶m2=val2")));
}
[Test]
public void Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values() {
Uri url = new Uri("http://www.domain.com/test#div");
var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test#div?param1=val1¶m2=val2")));
}
[Test]
public void Add_to_query_string_dictionary_when_url_contains_query_string_should_add_values_and_keep_original_query_string() {
Uri url = new Uri("http://www.domain.com/test?param1=val1");
var values = new Dictionary<string, string> { { "param2", "val2" } };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1¶m2=val2")));
}
[Test]
public void Add_to_query_string_dictionary_when_url_is_relative_contains_no_query_string_should_add_values() {
Uri url = new Uri("/test", UriKind.Relative);
var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1¶m2=val2", UriKind.Relative)));
}
[Test]
public void Add_to_query_string_dictionary_when_url_is_relative_and_contains_query_string_should_add_values_and_keep_original_query_string() {
Uri url = new Uri("/test?param1=val1", UriKind.Relative);
var values = new Dictionary<string, string> { { "param2", "val2" } };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1¶m2=val2", UriKind.Relative)));
}
[Test]
public void Add_to_query_string_dictionary_when_url_is_relative_and_contains_query_string_with_existing_value_should_add_new_values_and_update_existing_ones() {
Uri url = new Uri("/test?param1=val1", UriKind.Relative);
var values = new Dictionary<string, string> { { "param1", "new-value" }, { "param2", "val2" } };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("/test?param1=new-value¶m2=val2", UriKind.Relative)));
}
[Test]
public void Add_to_query_string_object_when_url_contains_no_query_string_should_add_values() {
Uri url = new Uri("http://www.domain.com/test");
var values = new { param1 = "val1", param2 = "val2" };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1¶m2=val2")));
}
[Test]
public void Add_to_query_string_object_when_url_contains_query_string_should_add_values_and_keep_original_query_string() {
Uri url = new Uri("http://www.domain.com/test?param1=val1");
var values = new { param2 = "val2" };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1¶m2=val2")));
}
[Test]
public void Add_to_query_string_object_when_url_is_relative_contains_no_query_string_should_add_values() {
Uri url = new Uri("/test", UriKind.Relative);
var values = new { param1 = "val1", param2 = "val2" };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1¶m2=val2", UriKind.Relative)));
}
[Test]
public void Add_to_query_string_object_when_url_is_relative_and_contains_query_string_should_add_values_and_keep_original_query_string() {
Uri url = new Uri("/test?param1=val1", UriKind.Relative);
var values = new { param2 = "val2" };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1¶m2=val2", UriKind.Relative)));
}
[Test]
public void Add_to_query_string_object_when_url_is_relative_and_contains_query_string_with_existing_value_should_add_new_values_and_update_existing_ones() {
Uri url = new Uri("/test?param1=val1", UriKind.Relative);
var values = new { param1 = "new-value", param2 = "val2" };
var result = url.ExtendQuery(values);
Assert.That(result, Is.EqualTo(new Uri("/test?param1=new-value¶m2=val2", UriKind.Relative)));
}
}