Boa*_*oaz 459 .net c# url query-string
从代码调用Web资源时的常见任务是构建查询字符串以包含所有必需参数.虽然无论如何都没有火箭科学,但是你需要注意一些漂亮的细节,&
如果不是第一个参数,编码参数等.
这样做的代码非常简单,但有点单调乏味:
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")); }
}
Run Code Online (Sandbox Code Playgroud)
这是一个常见的任务,人们期望实用程序类存在,使其更加优雅和可读.扫描MSDN,我找不到一个 - 这让我想到了以下问题:
你知道做上述事情最干净的方式是什么?
Joh*_*soe 658
您可以HttpValueCollection
通过调用创建一个新的可写实例System.Web.HttpUtility.ParseQueryString(string.Empty)
,然后将其用作任何实例NameValueCollection
.添加所需的值后,可以调用ToString
集合来获取查询字符串,如下所示:
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
Run Code Online (Sandbox Code Playgroud)
它HttpValueCollection
是内部的,因此您无法直接构造实例.但是,一旦获得实例,就可以像使用任何其他实例一样使用它NameValueCollection
.由于您正在使用的实际对象是一个HttpValueCollection
,因此调用ToString方法将调用重写的方法HttpValueCollection
,该方法将该集合格式化为URL编码的查询字符串.
在搜索SO和网络以寻找类似问题的答案之后,这是我能找到的最简单的解决方案.
.NET核心
如果您在.NET Core中工作,则可以使用Microsoft.AspNetCore.WebUtilities.QueryHelpers
该类,这极大地简化了这一过程.
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers
ann*_*ata 286
如果你看一下,QueryString属性是一个NameValueCollection.当我做了类似的事情时,我通常对序列化和反序列化感兴趣所以我的建议是建立一个NameValueCollection,然后传递给:
using System.Linq;
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);
}
Run Code Online (Sandbox Code Playgroud)
可能我可以格式化更好:)
我想在LINQ中有一个非常优雅的方式来做到这一点......
Ved*_*ran 88
在Roy Tinker的评论的启发下,我最终在Uri类上使用了一个简单的扩展方法,使我的代码简洁明了:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
Uri url = new Uri("http://localhost/rest/something/browse").
AddQuery("page", "0").
AddQuery("pageSize", "200");
Run Code Online (Sandbox Code Playgroud)
编辑 - 符合标准的变体
正如一些人所指出的那样,httpValueCollection.ToString()
以不符合标准的方式对Unicode字符进行编码.这是相同扩展方法的变体,它通过调用HttpUtility.UrlEncode
方法而不是弃用HttpUtility.UrlEncodeUnicode
方法来处理此类字符.
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;
}
Run Code Online (Sandbox Code Playgroud)
Met*_*ymy 62
奇怪的是没有人提到 AspNet.Core 中的 QueryBuilder。
当您有一个带有重复键的查询时,这会很有帮助,例如?filter=a&filter=b
var qb = new QueryBuilder();
qb.Add("filter", new string[] {"A", "B"});
Run Code Online (Sandbox Code Playgroud)
然后您只需将 qb 添加到 URI,它会自动转换为字符串。
Iga*_*nik 29
我刚才回答了类似的问题.基本上,最好的方法是使用HttpValueCollection
ASP.NET的Request.QueryString
属性实际上是类,不幸的是它是.NET框架内部的.您可以使用Reflector来抓取它(并将其放入Utils类中).这样您就可以像NameValueCollection那样操纵查询字符串,但是所有的url编码/解码问题都会得到照顾.
HttpValueCollection
extends NameValueCollection
,并且有一个构造函数,它接受一个编码的查询字符串(包括&符号和问号),并覆盖一个ToString()
方法,以便稍后从底层集合重建查询字符串.
例:
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
Run Code Online (Sandbox Code Playgroud)
Alf*_*red 28
这是一种流畅/ lambda-ish方式作为扩展方法(结合以前帖子中的概念),支持同一个键的多个值.我个人的偏好是对其他团队成员发现能力的包装器的扩展.请注意,围绕编码方法存在争议,在Stack Overflow(一个这样的帖子)和MSDN博客(如此一个)上有大量关于它的帖子.
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());
}
Run Code Online (Sandbox Code Playgroud)
编辑:支持null,但您可能需要根据您的具体情况进行调整
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;
}
Run Code Online (Sandbox Code Playgroud)
Tod*_*ier 22
Flurl [披露:我是作者]支持通过匿名对象(以及其他方式)构建查询字符串:
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!"
});
Run Code Online (Sandbox Code Playgroud)
可选的Flurl.Http伴侣库允许您在同一个流畅的调用链上进行HTTP调用,并将其扩展为完整的REST客户端:
T result = await "https://api.mysite.com"
.AppendPathSegment("person")
.SetQueryParams(new { ap_key = "my-key" })
.WithOAuthBearerToken("MyToken")
.PostJsonAsync(new { first_name = firstName, last_name = lastName })
.ReceiveJson<T>();
Run Code Online (Sandbox Code Playgroud)
NuGet提供完整的软件包:
PM> Install-Package Flurl.Http
或者只是独立的URL构建器:
PM> Install-Package Flurl
DSO*_*DSO 19
这是我迟到的条目.我出于各种原因不喜欢其他任何人,所以我写了自己的.
此版本的特点:
仅使用StringBuilder.没有ToArray()调用或其他扩展方法.它看起来并不像其他一些反应那么漂亮,但我认为这是一个核心功能,所以效率比拥有"流畅","单线"代码隐藏低效率更重要.
处理每个键的多个值.(我自己不需要它,只是为了让Mauricio保持沉默;)
public 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();
}
Run Code Online (Sandbox Code Playgroud) 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);
Run Code Online (Sandbox Code Playgroud)
http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26
Run Code Online (Sandbox Code Playgroud)
Luk*_*keH 11
如何创建允许您以这样流畅的方式添加参数的扩展方法?
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();
Run Code Online (Sandbox Code Playgroud)
这是使用的重载string
:
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);
}
Run Code Online (Sandbox Code Playgroud)
这是使用的重载StringBuilder
:
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));
}
Run Code Online (Sandbox Code Playgroud)
hor*_*man 11
我需要为我正在处理的可移植类库(PCL)解决同样的问题.在这种情况下,我无法访问System.Web,因此我无法使用ParseQueryString.
相反,我这样使用System.Net.Http.FormUrlEncodedContent
:
var url = new UriBuilder("http://example.com");
url.Query = new FormUrlEncodedContent(new Dictionary<string,string>()
{
{"param1", "val1"},
{"param2", "val2"},
{"param3", "val3"},
}).ReadAsStringAsync().Result;
Run Code Online (Sandbox Code Playgroud)
red*_*alx 10
在 dotnet core QueryHelpers.AddQueryString() 将接受键值对的 IDictionary<string,string> 。为了节省一些内存分配和 CPU 周期,您可以使用 SortedList<,> 而不是 Dictionary<,>,并按排序顺序添加适当的容量和项目...
var queryParams = new SortedList<string,string>(2);
queryParams.Add("abc", "val1");
queryParams.Add("def", "val2");
string requestUri = QueryHelpers.AddQueryString("https://localhost/api", queryParams);
Run Code Online (Sandbox Code Playgroud)
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());
}
Run Code Online (Sandbox Code Playgroud)
将此类添加到项目中
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()))));
}
}
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
var actual = new QueryStringBuilder {
{"foo", 123},
{"bar", "val31"},
{"bar", "val32"}
};
actual.Add("a+b", "c+d");
actual.ToString(); // "foo=123&bar=val31&bar=val32&a%2bb=c%2bd"
Run Code Online (Sandbox Code Playgroud)
我的提议:
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;
}
Run Code Online (Sandbox Code Playgroud)
用法:
var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method")
.AddQuery("wow", "soFluent");
// http://stackoverflow.com?such=method&wow=soFluent
Run Code Online (Sandbox Code Playgroud)
结合最佳答案来创建匿名对象版本:
var queryString = HttpUtility2.BuildQueryString(new
{
key2 = "value2",
key1 = "value1",
});
Run Code Online (Sandbox Code Playgroud)
这会产生这个:
键2=值2&键1=值1
这是代码:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
这里有很多好的答案,但对于那些使用现代 C# 的人来说,这可能是一个值得保留的不错的实用程序类。
public class QueryParamBuilder
{
private readonly Dictionary<string, string> _fields = new();
public QueryParamBuilder Add(string key, string value)
{
_fields.Add(key, value);
return this;
}
public string Build()
{
return $"?{String.Join("&", _fields.Select(pair => $"{HttpUtility.UrlEncode(pair.Key)}={HttpUtility.UrlEncode(pair.Value)}"))}";
}
public static QueryParamBuilder New => new();
}
Run Code Online (Sandbox Code Playgroud)
我在这里使用内部是Dictionary
因为字典在内部是可枚举的键值对,这使得迭代它们比NameValueCollection
.
那么查询字符串本身就是一个带有连接的简单内插字符串。
此外,我在构造函数中提供了一个静态接口,使新构建器的构造变得非常容易,并且只允许一种公开的方法Add
添加新的查询参数值。最后,您终止链以Build()
实际获得最终的字符串。
这是其用法的示例
var queryString = QueryParamBuilder.New
.Add("id", "0123")
.Add("value2", 1234.ToString())
.Add("valueWithSpace","value with spa12!@#@!ce")
.Build();
Run Code Online (Sandbox Code Playgroud)
结果正如预期的那样
?id=0123&value2=1234&valueWithSpace=value+with+spa12!%40%23%40!ce
Run Code Online (Sandbox Code Playgroud)
希望你们中的一些人会发现这美好而优雅。
未经测试,但我认为沿着这些方向的东西可以很好地工作
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();
Run Code Online (Sandbox Code Playgroud)
基于快速扩展方法的版本:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用 where 子句来选择将哪些参数添加到字符串中。
归档时间: |
|
查看次数: |
305174 次 |
最近记录: |