Jawaban yang diberikan memiliki masalah dengan Url relatif, seperti "/ some / path /" Ini adalah batasan dari kelas Uri dan UriBuilder, yang agak sulit dimengerti, karena saya tidak melihat alasan mengapa url relatif akan bermasalah. ketika datang ke manipulasi permintaan.
Berikut adalah solusi yang berfungsi untuk jalur absolut dan relatif, ditulis dan diuji dalam .NET 4:
(catatan kecil: ini juga harus bekerja di .NET 4.5, Anda hanya perlu mengubah propInfo.GetValue(values, null)
ke 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; }
));
}
}
Dan di sini ada serangkaian unit test untuk menguji perilaku:
[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)));
}
}