How to build a query string for a URL in C#?
从代码调用Web资源时的一个常见任务是构建一个查询字符串,以包含所有必需的参数。尽管绝对没有火箭科学,但你需要注意一些漂亮的细节,比如,如果不是第一个参数,附加一个
要做的代码非常简单,但有点乏味:
1 2 3 4 5 6 7 8 9 10 11 | StringBuilder SB = new StringBuilder(); if (NeedsToAddParameter A) { SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA")); } if (NeedsToAddParameter B) { if (SB.Length>0) SB.Append("&"); SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); } } |
这是一个很常见的任务,人们希望存在一个实用程序类,使它更加优雅和可读。扫描msdn时,我找不到一个—,这使我遇到了以下问题:
你所知道的最优雅、最干净的方法是什么?
您可以通过调用
1 2 3 4 5 6 | NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty); queryString["key1"] ="value1"; queryString["key2"] ="value2"; return queryString.ToString(); // Returns"key1=value1&key2=value2", all URL-encoded |
在搜索了So和Web上类似问题的答案之后,这是我能找到的最简单的解决方案。
网络核心
如果您在.NET核心中工作,那么可以使用
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers
如果您查看引擎盖下的QueryString属性是一个NameValueCollection。当我做过类似的事情时,我通常对序列化和反序列化感兴趣,因此我的建议是建立一个NameValueCollection,然后传递给:
1 2 3 4 5 6 7 8 9 10 11 | 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); } |
也许我可以把它格式化得更好。)
我想在林肯也有一个非常优雅的方式来做到这一点…
在Roy Tinker的评论的启发下,我最终在URI类上使用了一个简单的扩展方法,使代码简洁明了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System.Web; public static class HttpExtensions { public static Uri AddQuery(this Uri uri, string name, string value) { var httpValueCollection = HttpUtility.ParseQueryString(uri.Query); httpValueCollection.Remove(name); httpValueCollection.Add(name, value); var ub = new UriBuilder(uri); ub.Query = httpValueCollection.ToString(); return ub.Uri; } } |
用途:
1 2 3 | Uri url = new Uri("http://localhost/rest/something/browse"). AddQuery("page","0"). AddQuery("pageSize","200"); |
编辑-符合标准的变体
正如一些人指出的,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | using System.Web; public static Uri AddQuery(this Uri uri, string name, string value) { var httpValueCollection = HttpUtility.ParseQueryString(uri.Query); httpValueCollection.Remove(name); httpValueCollection.Add(name, value); var ub = new UriBuilder(uri); // this code block is taken from httpValueCollection.ToString() method // and modified so it encodes strings with HttpUtility.UrlEncode if (httpValueCollection.Count == 0) ub.Query = String.Empty; else { var sb = new StringBuilder(); for (int i = 0; i < httpValueCollection.Count; i++) { string text = httpValueCollection.GetKey(i); { text = HttpUtility.UrlEncode(text); string val = (text != null) ? (text +"=") : string.Empty; string[] vals = httpValueCollection.GetValues(i); if (sb.Length > 0) sb.Append('&'); if (vals == null || vals.Length == 0) sb.Append(val); else { if (vals.Length == 1) { sb.Append(val); sb.Append(HttpUtility.UrlEncode(vals[0])); } else { for (int j = 0; j < vals.Length; j++) { if (j > 0) sb.Append('&'); sb.Append(val); sb.Append(HttpUtility.UrlEncode(vals[j])); } } } } } ub.Query = sb.ToString(); } return ub.Uri; } |
我刚才回答了一个类似的问题。基本上,最好的方法是使用类
例子:
1 2 3 4 5 6 7 8 9 | var coll = new HttpValueCollection(); coll["userId"] ="50"; coll["paramA"] ="A"; coll["paramB"] ="B"; string query = coll.ToString(true); // true means use urlencode Console.WriteLine(query); // prints: userId=50¶mA=A¶mB=B |
这里有一个fluent/lambda风格的扩展方法(结合前面文章中的概念),它支持同一个键的多个值。我个人的偏好是扩展包装器,使其他团队成员能够发现类似的东西。请注意,关于编码方法存在争议,在StackOverflow(一个这样的帖子)和msdn bloggers(像这个帖子)上有很多关于它的帖子。
1 2 3 4 5 6 7 | public static string ToQueryString(this NameValueCollection source) { return String.Join("&", source.AllKeys .SelectMany(key => source.GetValues(key) .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)))) .ToArray()); } |
编辑:使用空支持,尽管您可能需要根据特定情况调整它
1 2 3 4 5 6 7 8 9 10 11 12 | public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries) { return source != null ? String.Join("&", source.AllKeys .Where(key => !removeEmptyEntries || source.GetValues(key) .Where(value => !String.IsNullOrEmpty(value)) .Any()) .SelectMany(key => source.GetValues(key) .Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value)) .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty))) .ToArray()) : string.Empty; } |
flurl[公开:我是作者]支持通过匿名对象构建查询字符串(以及其他方式):
1 2 3 4 5 6 | var url ="http://www.some-api.com".SetQueryParams(new { api_key = ConfigurationManager.AppSettings["SomeApiKey"], max_results = 20, q ="Don't worry, I'll get encoded!" }); |
可选flurl.http companion lib允许您直接从同一个fluent调用链执行HTTP调用,并将其扩展到一个完整的REST客户机:
1 2 3 4 5 6 |
Nuget上提供完整的软件包:
或者只是独立的URL生成器:
这是我迟到的地方。由于各种原因,我不喜欢其他人,所以我写了自己的。
此版本的功能:
仅使用StringBuilder。没有ToArray()调用或其他扩展方法。它看起来不像其他一些响应那么漂亮,但我认为这是一个核心功能,因此效率比拥有"流畅的"、"一行程序"代码(隐藏效率低下)更重要。
每个键处理多个值。(我自己不需要,只是想让毛里西奥闭嘴;)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public string ToQueryString(NameValueCollection nvc)
{
StringBuilder sb = new StringBuilder("?");
bool first = true;
foreach (string key in nvc.AllKeys)
{
foreach (string value in nvc.GetValues(key))
{
if (!first)
{
sb.Append("&");
}
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
first = false;
}
}
return sb.ToString();
}
示例用法
1 2 3 4 5 6 7 8 9 10 11 12 | var queryParams = new NameValueCollection() { {"x","1" }, {"y","2" }, {"foo","bar" }, {"foo","baz" }, {"special chars","? = &" }, }; string url ="http://example.com/stuff" + ToQueryString(queryParams); Console.WriteLine(url); |
产量
1 | http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26 |
I needed to solve the same problem for a portable class library (PCL) that I'm working on. In this case, I don't have access to System.Web so I can't use ParseQueryString.
Instead I used
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var url = new UriBuilder("http://example.com"); url.Query = new FormUrlEncodedContent(new Dictionary<string,string>() { {"param1","val1 <div class="suo-content">[collapse title=""]<ul><li>这是我使用的技术,并在另一个问题http://stackoverflow.com/a/26744471/2108310中引用了它。唯一的区别是我使用了一个键值对数组…除了需要对System.net的引用(如您所述,它是可用的PCL)之外,这是一种最简单的方法,它不包括一些第三方软件包,或者尝试将自制的意大利面拼凑在一起。</li></ul>[/collapse]</div><hr><P>如何创建扩展方法,允许您以这样一种流畅的样式添加参数?</P>[cc lang="csharp"]string a ="http://www.somedomain.com/somepage.html" .AddQueryParam("A","TheValueOfA") .AddQueryParam("B","TheValueOfB") .AddQueryParam("Z","TheValueOfZ"); string b = new StringBuilder("http://www.somedomain.com/anotherpage.html") .AddQueryParam("A","TheValueOfA") .AddQueryParam("B","TheValueOfB") .AddQueryParam("Z","TheValueOfZ") .ToString(); |
下面是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public static string AddQueryParam( this string source, string key, string value) { string delim; if ((source == null) || !source.Contains("?")) { delim ="?"; } else if (source.EndsWith("?") || source.EndsWith("&")) { delim = string.Empty; } else { delim ="&"; } return source + delim + HttpUtility.UrlEncode(key) +"=" + HttpUtility.UrlEncode(value); } |
下面是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public static StringBuilder AddQueryParam( this StringBuilder source, string key, string value) { bool hasQuery = false; for (int i = 0; i < source.Length; i++) { if (source[i] == '?') { hasQuery = true; break; } } string delim; if (!hasQuery) { delim ="?"; } else if ((source[source.Length - 1] == '?') || (source[source.Length - 1] == '&')) { delim = string.Empty; } else { delim ="&"; } return source.Append(delim).Append(HttpUtility.UrlEncode(key)) .Append("=").Append(HttpUtility.UrlEncode(value)); } |
1 2 3 4 5 6 7 8 9 | public static string ToQueryString(this Dictionary<string, string> source) { return String.Join("&", source.Select(kvp => String.Format("{0}={1}", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))).ToArray()); } public static string ToQueryString(this NameValueCollection source) { return String.Join("&", source.Cast<string>().Select(key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(source[key]))).ToArray()); } |
将此类添加到项目中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | using System; using System.Collections.Generic; using System.Linq; using System.Web; public class QueryStringBuilder { private readonly List<KeyValuePair<string, object>> _list; public QueryStringBuilder() { _list = new List<KeyValuePair<string, object>>(); } public void Add(string name, object value) { _list.Add(new KeyValuePair<string, object>(name, value)); } public override string ToString() { return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key),"=", Uri.EscapeDataString(kvp.Value.ToString())))); } } |
像这样使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var actual = new QueryStringBuilder { {"foo", 123}, {"bar","val31 <div class="suo-content">[collapse title=""]<ul><li>现在,这应该是公认的答案;对于"foo[]=1,foo[]=2"这样的数组,以及保持参数顺序非常重要。</li></ul>[/collapse]</div><hr><P>我的奉献:</P>[cc lang="csharp"]public static Uri AddQuery(this Uri uri, string name, string value) { // this actually returns HttpValueCollection : NameValueCollection // which uses unicode compliant encoding on ToString() var query = HttpUtility.ParseQueryString(uri.Query); query.Add(name, value); var uriBuilder = new UriBuilder(uri) { Query = query.ToString() }; return uriBuilder.Uri; } |
用途:
1 2 3 4 | var uri = new Uri("http://stackoverflow.com").AddQuery("such","method") .AddQuery("wow","soFluent"); // http://stackoverflow.com?such=method&wow=soFluent |
未经测试,但我认为这方面的工作会很好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class QueryString { private Dictionary<string,string> _Params = new Dictionary<string,string>(); public overide ToString() { List<string> returnParams = new List<string>(); foreach (KeyValuePair param in _Params) { returnParams.Add(String.Format("{0}={1}", param.Key, param.Value)); } // return String.Format("?{0}", String.Join("&", returnParams.ToArray())); // credit annakata return"?" + String.Join("&", returnParams.ToArray()); } public void Add(string key, string value) { _Params.Add(key, HttpUtility.UrlEncode(value)); } } QueryString query = new QueryString(); query.Add("param1","value1"); query.Add("param2","value2"); return query.ToString(); |
基于快速扩展方法的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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); } } |
可以使用WHERE子句选择要添加到字符串中的参数。
假设要减少对其他程序集的依赖性并保持简单,可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 | var sb = new System.Text.StringBuilder(); sb.Append("a=" + HttpUtility.UrlEncode("TheValueOfA") +"&"); sb.Append("b=" + HttpUtility.UrlEncode("TheValueOfB") +"&"); sb.Append("c=" + HttpUtility.UrlEncode("TheValueOfC") +"&"); sb.Append("d=" + HttpUtility.UrlEncode("TheValueOfD") +"&"); sb.Remove(sb.Length-1, 1); // Remove the final '&' string result = sb.ToString(); |
这也适用于循环。最后一个与符号的删除需要超出循环。
请注意,串联运算符用于提高可读性。与使用StringBuilder相比,使用它的成本是最小的(我认为JeffAtwood在这个主题上发布了一些内容)。
结合最重要的答案创建匿名对象版本:
1 2 3 4 5 |
这就产生了:
key2=value2&key1=value1
< /块引用>
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 public static class HttpUtility2
{
public static string BuildQueryString<T>(T obj)
{
var queryString = HttpUtility.ParseQueryString(string.Empty);
foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>())
{
var value = (property.GetValue(obj) ??"").ToString();
queryString.Add(property.Name, value);
}
return queryString.ToString();
}
}我有一个URI的扩展方法:
- 接受匿名对象:
uri.WithQuery(new { name ="value" }) 。- 接受
string/string 对的集合(例如字典'2)。- 接受
string/object 对的集合(例如routeValueDictionary)。- 接受NameValueCollections。
- 按键对查询值排序,以便相同的值产生相同的URI。
- 每个键支持多个值,保留其原始顺序。
文档版本可以在这里找到。
扩展:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 public static Uri WithQuery(this Uri uri, object values)
{
if (uri == null)
throw new ArgumentNullException(nameof(uri));
if (values != null)
{
var query = string.Join(
"&", from p in ParseQueryValues(values)
where !string.IsNullOrWhiteSpace(p.Key)
let k = HttpUtility.UrlEncode(p.Key.Trim())
let v = HttpUtility.UrlEncode(p.Value)
orderby k
select string.IsNullOrEmpty(v) ? k : $"{k}={v}");
if (query.Length != 0 || uri.Query.Length != 0)
uri = new UriBuilder(uri) { Query = query }.Uri;
}
return uri;
}查询分析器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 private static IEnumerable<KeyValuePair<string, string>> ParseQueryValues(object values)
{
// Check if a name/value collection.
var nvc = values as NameValueCollection;
if (nvc != null)
return from key in nvc.AllKeys
from val in nvc.GetValues(key)
select new KeyValuePair<string, string>(key, val);
// Check if a string/string dictionary.
var ssd = values as IEnumerable<KeyValuePair<string, string>>;
if (ssd != null)
return ssd;
// Check if a string/object dictionary.
var sod = values as IEnumerable<KeyValuePair<string, object>>;
if (sod == null)
{
// Check if a non-generic dictionary.
var ngd = values as IDictionary;
if (ngd != null)
sod = ngd.Cast<dynamic>().ToDictionary<dynamic, string, object>(
p => p.Key.ToString(), p => p.Value as object);
// Convert object properties to dictionary.
if (sod == null)
sod = new RouteValueDictionary(values);
}
// Normalize and return the values.
return from pair in sod
from val in pair.Value as IEnumerable<string>
?? new[] { pair.Value?.ToString() }
select new KeyValuePair<string, string>(pair.Key, val);
}以下是测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 var uri = new Uri("https://stackoverflow.com/yo?oldKey=oldValue");
// Test with a string/string dictionary.
var q = uri.WithQuery(new Dictionary<string, string>
{
["k1"] = string.Empty,
["k2"] = null,
["k3"] ="v3"
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1&k2&k3=v3"));
// Test with a string/object dictionary.
q = uri.WithQuery(new Dictionary<string, object>
{
["k1"] ="v1",
["k2"] = new[] {"v2a","v2b" },
["k3"] = null
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3"));
// Test with a name/value collection.
var nvc = new NameValueCollection()
{
["k1"] = string.Empty,
["k2"] ="v2a"
};
nvc.Add("k2","v2b");
q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1&k2=v2a&k2=v2b"));
// Test with any dictionary.
q = uri.WithQuery(new Dictionary<int, HashSet<string>>
{
[1] = new HashSet<string> {"v1" },
[2] = new HashSet<string> {"v2a","v2b" },
[3] = null
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?1=v1&2=v2a&2=v2b&3"));
// Test with an anonymous object.
q = uri.WithQuery(new
{
k1 ="v1",
k2 = new[] {"v2a","v2b" },
k3 = new List<string> {"v3" },
k4 = true,
k5 = null as Queue<string>
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3=v3&k4=True&k5"));
// Keep existing query using a name/value collection.
nvc = HttpUtility.ParseQueryString(uri.Query);
nvc.Add("newKey","newValue");
q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?newKey=newValue&oldKey=oldValue"));
// Merge two query objects using the RouteValueDictionary.
var an1 = new { k1 ="v1" };
var an2 = new { k2 ="v2" };
q = uri.WithQuery(
new RouteValueDictionary(an1).Concat(
new RouteValueDictionary(an2)));
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2"));[还有迟到]
httpValueCollection的可链包装类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 namespace System.Web.Mvc {
public class QueryStringBuilder {
private NameValueCollection collection;
public QueryStringBuilder() {
collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
}
public QueryStringBuilder Add(string key, string value) {
collection.Add(key, value);
return this;
}
public QueryStringBuilder Remove(string key) {
collection.Remove(key);
return this;
}
public string this[string key] {
get { return collection[key]; }
set { collection[key] = value; }
}
public string ToString() {
return collection.ToString();
}
}
}示例用法:
1
2
3
4
5 QueryStringBuilder parameters = new QueryStringBuilder()
.Add("view", ViewBag.PageView)
.Add("page", ViewBag.PageNumber)
.Add("size", ViewBag.PageSize);
string queryString = parameters.ToString();
Same as accepted solution, but transfred to"dot" LINQ syntax...
ZZU1〔0〕
我在pageBase类中添加了以下方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 protected void Redirect(string url)
{
Response.Redirect(url);
}
protected void Redirect(string url, NameValueCollection querystrings)
{
StringBuilder redirectUrl = new StringBuilder(url);
if (querystrings != null)
{
for (int index = 0; index < querystrings.Count; index++)
{
if (index == 0)
{
redirectUrl.Append("?");
}
redirectUrl.Append(querystrings.Keys[index]);
redirectUrl.Append("=");
redirectUrl.Append(HttpUtility.UrlEncode(querystrings[index]));
if (index < querystrings.Count - 1)
{
redirectUrl.Append("&");
}
}
}
this.Redirect(redirectUrl.ToString());
}致电:
1
2
3
4 NameValueCollection querystrings = new NameValueCollection();
querystrings.Add("language","en");
querystrings.Add("id","134");
this.Redirect("http://www.mypage.com", querystrings);我使用了DSO提出的解决方案(在2011年8月2日7:29回答),他的解决方案不需要使用httputility。然而,根据dotnetearls上发表的一篇文章,使用字典比使用namevaluecollection更快(在性能上)。这里是DSO的解决方案,修改后使用字典代替NameValueCollection。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 public static Dictionary<string, string> QueryParametersDictionary()
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("name","John Doe");
dictionary.Add("address.city","Seattle");
dictionary.Add("address.state_code","WA");
dictionary.Add("api_key","5352345263456345635");
return dictionary;
}
public static string ToQueryString(Dictionary<string, string> nvc)
{
StringBuilder sb = new StringBuilder();
bool first = true;
foreach (KeyValuePair<string, string> pair in nvc)
{
if (!first)
{
sb.Append("&");
}
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value));
first = false;
}
return sb.ToString();
}我编写了一些扩展方法,这些方法在使用querystrings时非常有用。通常我想从当前的querystring开始,并在使用它之前进行修改。有点像
1
2
3
4
5 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更多信息和来源:http://www.charlesrcok.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx
它很基本,但我喜欢它的风格。
1
2
3
4
5
6
7
8
9
10
11 // 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"));
}输出:
http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue
代码;你们都可以在某个地方感谢我,不知何故:D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 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);
}
}
}
Just wanted to throw in my 2 cents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 public static class HttpClientExt
{
public static Uri AddQueryParams(this Uri uri, string query)
{
var ub = new UriBuilder(uri);
ub.Query = string.IsNullOrEmpty(uri.Query) ? query : string.Join("&", uri.Query.Substring(1), query);
return ub.Uri;
}
public static Uri AddQueryParams(this Uri uri, IEnumerable<string> query)
{
return uri.AddQueryParams(string.Join("&", query));
}
public static Uri AddQueryParams(this Uri uri, string key, string value)
{
return uri.AddQueryParams(string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)));
}
public static Uri AddQueryParams(this Uri uri, params KeyValuePair<string,string>[] kvps)
{
return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
}
public static Uri AddQueryParams(this Uri uri, IDictionary<string, string> kvps)
{
return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
}
public static Uri AddQueryParams(this Uri uri, NameValueCollection nvc)
{
return uri.AddQueryParams(nvc.AllKeys.SelectMany(nvc.GetValues, (key, value) => string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))));
}
}文件说,如果
uri.Query 不是空的,那么它将从? 开始,如果你要修改它,那么你应该删掉它。注意,
HttpUtility.UrlEncode 出现在System.Web 中。用途:
1 var uri = new Uri("https://api.del.icio.us/v1/posts/suggest").AddQueryParam("url","http://stackoverflow.com")适用于NameValueCollection中的每个键的多个值。
例如:
{ {"k1","v1 我用其他答案的一些提示为我的剃须刀项目写了一个助手。
ParseQueryString业务是必需的,因为我们不允许篡改当前请求的QueryString对象。
1
2
3
4
5 @helper GetQueryStringWithValue(string key, string value) {
var queryString = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString());
queryString[key] = value;
@Html.Raw(queryString.ToString())
}我是这样使用的:
1如果您希望它获取多个值,只需将参数更改为字典并将对添加到查询字符串中即可。
虽然不优雅,但我选择了一个更简单的版本,它不使用
NameValueCollecitons ,只是一个围绕StringBuilder 的构建器模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 public class UrlBuilder
{
#region Variables / Properties
private readonly StringBuilder _builder;
#endregion Variables / Properties
#region Constructor
public UrlBuilder(string urlBase)
{
_builder = new StringBuilder(urlBase);
}
#endregion Constructor
#region Methods
public UrlBuilder AppendParameter(string paramName, string value)
{
if (_builder.ToString().Contains("?"))
_builder.Append("&");
else
_builder.Append("?");
_builder.Append(HttpUtility.UrlEncode(paramName));
_builder.Append("=");
_builder.Append(HttpUtility.UrlEncode(value));
return this;
}
public override string ToString()
{
return _builder.ToString();
}
#endregion Methods
}根据现有答案,我确保使用
HttpUtility.UrlEncode 电话。它的用法是这样的:
1
2
3
4
5
6 string url = new UrlBuilder("http://www.somedomain.com/")
.AppendParameter("a","true")
.AppendParameter("b","muffin")
.AppendParameter("c","muffin button")
.ToString();
// Result: http://www.somedomain.com?a=true&b=muffin&c=muffin%20button
This is another (maybe redundant :-]) way for do that.
The conceptuals are the same of the Vedran answer in this page (take a look here).
But this class is more efficient, because it iterate through all Keys only one time: when
ToString is invoked.The formatting code is also semplified and improved.
Hope that could be helpful.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public sealed class QueryStringBuilder
{
public QueryStringBuilder()
{
this.inner = HttpUtility.ParseQueryString(string.Empty);
}
public QueryStringBuilder(string queryString)
{
this.inner = HttpUtility.ParseQueryString(queryString);
}
public QueryStringBuilder(string queryString, Encoding encoding)
{
this.inner = HttpUtility.ParseQueryString(queryString, encoding);
}
private readonly NameValueCollection inner;
public QueryStringBuilder AddKey(string key, string value)
{
this.inner.Add(key, value);
return this;
}
public QueryStringBuilder RemoveKey(string key)
{
this.inner.Remove(key);
return this;
}
public QueryStringBuilder Clear()
{
this.inner.Clear();
return this;
}
public override String ToString()
{
if (this.inner.Count == 0)
return string.Empty;
var builder = new StringBuilder();
for (int i = 0; i < this.inner.Count; i++)
{
if (builder.Length > 0)
builder.Append('&');
var key = this.inner.GetKey(i);
var values = this.inner.GetValues(i);
if (key == null || values == null || values.Length == 0)
continue;
for (int j = 0; j < values.Length; j++)
{
if (j > 0)
builder.Append('&');
builder.Append(HttpUtility.UrlEncode(key));
builder.Append('=');
builder.Append(HttpUtility.UrlEncode(values[j]));
}
}
return builder.ToString();
}
}这与公认的答案相同,只是稍微紧凑一点:
1
2
3
4
5
6 private string ToQueryString(NameValueCollection nvc)
{
return"?" + string.Join("&", nvc.AllKeys.Select(k => string.Format("{0}={1}",
HttpUtility.UrlEncode(k),
HttpUtility.UrlEncode(nvc[k]))));
}下面的代码是通过ilspy从
ToString 的HttpValueCollection 实现中去掉的,它为您提供了一个名称=value querystring。不幸的是,httpValueCollection是一个内部类,只有在使用
HttpUtility.ParseQueryString() 时才能返回。我删除了它的所有viewstate部分,默认情况下它会进行编码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public static class HttpExtensions
{
public static string ToQueryString(this NameValueCollection collection)
{
// This is based off the NameValueCollection.ToString() implementation
int count = collection.Count;
if (count == 0)
return string.Empty;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < count; i++)
{
string text = collection.GetKey(i);
text = HttpUtility.UrlEncodeUnicode(text);
string value = (text != null) ? (text +"=") : string.Empty;
string[] values = collection.GetValues(i);
if (stringBuilder.Length > 0)
{
stringBuilder.Append('&');
}
if (values == null || values.Length == 0)
{
stringBuilder.Append(value);
}
else
{
if (values.Length == 1)
{
stringBuilder.Append(value);
string text2 = values[0];
text2 = HttpUtility.UrlEncodeUnicode(text2);
stringBuilder.Append(text2);
}
else
{
for (int j = 0; j < values.Length; j++)
{
if (j > 0)
{
stringBuilder.Append('&');
}
stringBuilder.Append(value);
string text2 = values[j];
text2 = HttpUtility.UrlEncodeUnicode(text2);
stringBuilder.Append(text2);
}
}
}
}
return stringBuilder.ToString();
}
}只适用于那些需要vb.net版本的顶级答案的用户:
1
2
3
4 Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
Dim array As String() = nvc.AllKeys.SelectMany(Function(key As String) nvc.GetValues(key), Function(key As String, value As String) String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key), System.Web.HttpUtility.UrlEncode(value))).ToArray()
Return"?" + String.Join("&", array)
End Function没有LINQ的版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
Dim lsParams As New List(Of String)()
For Each strKey As String In nvc.AllKeys
Dim astrValue As String() = nvc.GetValues(strKey)
For Each strValue As String In astrValue
lsParams.Add(String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)))
Next ' Next strValue
Next ' strKey
Dim astrParams As String() = lsParams.ToArray()
lsParams.Clear()
lsParams = Nothing
Return"?" + String.Join("&", astrParams)
End Function ' ToQueryString以及不带LINQ的C版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 public static string ToQueryString(System.Collections.Specialized.NameValueCollection nvc)
{
List<string> lsParams = new List<string>();
foreach (string strKey in nvc.AllKeys)
{
string[] astrValue = nvc.GetValues(strKey);
foreach (string strValue in astrValue)
{
lsParams.Add(string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)));
} // Next strValue
} // Next strKey
string[] astrParams =lsParams.ToArray();
lsParams.Clear();
lsParams = null;
return"?" + string.Join("&", astrParams);
} // End Function ToQueryString这里是一个使用非常基本语言特性的实现。它是一个类的一部分,我们必须在目标C中移植和维护它,因此我们选择有更多的代码行,但是对于不太熟悉C的程序员来说,移植和理解更容易。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 /// <summary>
/// Builds a complete http url with query strings.
/// </summary>
/// <param name="pHostname"></param>
/// <param name="pPort"></param>
/// <param name="pPage">ex"/index.html" or index.html</param>
/// <param name="pGetParams">a Dictionary<string,string> collection containing the key value pairs. Pass null if there are none.</param>
/// <returns>a string of the form: http://[pHostname]:[pPort/[pPage]?key1=val1&key2=val2...</returns>
static public string buildURL(string pHostname, int pPort, string pPage, Dictionary<string,string> pGetParams)
{
StringBuilder sb = new StringBuilder(200);
sb.Append("http://");
sb.Append(pHostname);
if( pPort != 80 ) {
sb.Append(pPort);
}
// Allows page param to be passed in with or without leading slash.
if( !pPage.StartsWith("/") ) {
sb.Append("/");
}
sb.Append(pPage);
if (pGetParams != null && pGetParams.Count > 0)
{
sb.Append("?");
foreach (KeyValuePair<string, string> kvp in pGetParams)
{
sb.Append(kvp.Key);
sb.Append("=");
sb.Append( System.Web.HttpUtility.UrlEncode(kvp.Value) );
sb.Append("&");
}
sb.Remove(sb.Length - 1, 1); // Remove the final '&'
}
return sb.ToString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 public string UrlQueryStr(object data)
{
if (data == null)
return string.Empty;
object val;
StringBuilder sb = new StringBuilder();
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(data))
{
if ((val = prop.GetValue(data)) != null)
{
sb.AppendFormat("{0}{1}={2}", sb.Length == 0 ? '?' : '&',
HttpUtility.UrlEncode(prop.Name), HttpUtility.UrlEncode(val.ToString()));
}
}
return sb.ToString();
}编辑-正如评论中指出的,这不是一个好方法。
有这样一个类——URI类。"提供统一资源标识符(URI)的对象表示形式,并易于访问URI的各个部分。"(Microsoft文档)。
下面的示例创建一个URI类的实例,并使用它创建一个WebRequest实例。
C例
uri siteuri=new uri("http://www.contoso.com/");
webrequest wr=webrequest.create(siteuri);
看看,这个类有很多方法。