HttpUtility.ParseQueryString的可移植类库(PCL)版本

是否有System.Web中包含的HttpUtility.ParseQueryString的可移植类库(PCL)版本或我可以使用的一些代码? 我想读一个非常复杂的URL。

HttpUtility.ParseQueryString返回inheritance自NameValueCollection HttpValueCollection (Internal Class)。 NameValueCollection是一个键值对的集合,如字典,但它支持重复,维护顺序,只实现IEnumerable (此集合是pre-generics)。 PCL不支持NameValueCollection

我的解决方案(部分解除并从.NET框架修改)是用Collection替换HttpValueCollection,其中HttpValue只是一个键值对。

 public sealed class HttpUtility { public static HttpValueCollection ParseQueryString(string query) { if (query == null) { throw new ArgumentNullException("query"); } if ((query.Length > 0) && (query[0] == '?')) { query = query.Substring(1); } return new HttpValueCollection(query, true); } } public sealed class HttpValue { public HttpValue() { } public HttpValue(string key, string value) { this.Key = key; this.Value = value; } public string Key { get; set; } public string Value { get; set; } } public class HttpValueCollection : Collection { #region Constructors public HttpValueCollection() { } public HttpValueCollection(string query) : this(query, true) { } public HttpValueCollection(string query, bool urlencoded) { if (!string.IsNullOrEmpty(query)) { this.FillFromString(query, urlencoded); } } #endregion #region Parameters public string this[string key] { get { return this.First(x => string.Equals(x.Key, key, StringComparison.OrdinalIgnoreCase)).Value; } set { this.First(x => string.Equals(x.Key, key, StringComparison.OrdinalIgnoreCase)).Value = value; } } #endregion #region Public Methods public void Add(string key, string value) { this.Add(new HttpValue(key, value)); } public bool ContainsKey(string key) { return this.Any(x => string.Equals(x.Key, key, StringComparison.OrdinalIgnoreCase)); } public string[] GetValues(string key) { return this.Where(x => string.Equals(x.Key, key, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).ToArray(); } public void Remove(string key) { this.Where(x => string.Equals(x.Key, key, StringComparison.OrdinalIgnoreCase)) .ToList() .ForEach(x => this.Remove(x)); } public override string ToString() { return this.ToString(true); } public virtual string ToString(bool urlencoded) { return this.ToString(urlencoded, null); } public virtual string ToString(bool urlencoded, IDictionary excludeKeys) { if (this.Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (HttpValue item in this) { string key = item.Key; if ((excludeKeys == null) || !excludeKeys.Contains(key)) { string value = item.Value; if (urlencoded) { // If .NET 4.5 and above (Thanks @Paya) key = WebUtility.UrlDecode(key); // If .NET 4.0 use this instead. // key = Uri.EscapeDataString(key); } if (stringBuilder.Length > 0) { stringBuilder.Append('&'); } stringBuilder.Append((key != null) ? (key + "=") : string.Empty); if ((value != null) && (value.Length > 0)) { if (urlencoded) { value = Uri.EscapeDataString(value); } stringBuilder.Append(value); } } } return stringBuilder.ToString(); } #endregion #region Private Methods private void FillFromString(string query, bool urlencoded) { int num = (query != null) ? query.Length : 0; for (int i = 0; i < num; i++) { int startIndex = i; int num4 = -1; while (i < num) { char ch = query[i]; if (ch == '=') { if (num4 < 0) { num4 = i; } } else if (ch == '&') { break; } i++; } string str = null; string str2 = null; if (num4 >= 0) { str = query.Substring(startIndex, num4 - startIndex); str2 = query.Substring(num4 + 1, (i - num4) - 1); } else { str2 = query.Substring(startIndex, i - startIndex); } if (urlencoded) { this.Add(Uri.UnescapeDataString(str), Uri.UnescapeDataString(str2)); } else { this.Add(str, str2); } if ((i == (num - 1)) && (query[i] == '&')) { this.Add(null, string.Empty); } } } #endregion } 

UPDATE

更新,以便HttpValueCollection现在inheritance自Collection而不是List,如注释中突出显示的那样。

更新2

更新为使用WebUtility.UrlDecode,如果使用.NET 4.5,感谢@Paya。

您也可以像这样实现它:

 public static class HttpUtility { public static Dictionary ParseQueryString(Uri uri) { var query = uri.Query.Substring(uri.Query.IndexOf('?') + 1); // +1 for skipping '?' var pairs = query.Split('&'); return pairs .Select(o => o.Split('=')) .Where(items => items.Count() == 2) .ToDictionary(pair => Uri.UnescapeDataString(pair[0]), pair => Uri.UnescapeDataString(pair[1])); } } 

这是一个unit testing:

 public class HttpParseQueryValuesTests { [TestCase("http://www.example.com", 0, "", "")] [TestCase("http://www.example.com?query=value", 1, "query", "value")] public void When_parsing_http_query_then_should_have_these_values(string uri, int expectedParamCount, string expectedKey, string expectedValue) { var queryParams = HttpUtility.ParseQueryString(new Uri(uri)); queryParams.Count.Should().Be(expectedParamCount); if (queryParams.Count > 0) queryParams[expectedKey].Should().Be(expectedValue); } } 

我的Flurl库是一个PCL,当您从字符串中实例化Url对象时IDictionary它会将查询字符串解析为IDictionary

 using Flurl; var url = new Url("http://..."); // get values from url.QueryParams dictionary 

相关的解析逻辑就在这里 。 Flurl很小,但如果你愿意,可以随意刷一下这些。

我今天制作了一个nuget包,它可以进行基本的查询构建和解析。 它是为个人使用而制作的,但可从nuget.com repo获得。 对于个人使用,意味着它可能不完全符合’http查询规范’。 Nuget链接在这里

它基于字典,因此不支持重复键,主要是因为我不知道为什么你会想要…(任何人都可以启发我吗?)

它有1个类,表示支持添加,获取参数,检查它是否包含密钥的查询…以及解析密钥并返回查询实例的静态方法。