Есть ли версия библиотеки портативных классов (PCL) HttpUtility.ParseQueryString, содержащаяся в System.Web или какой-нибудь код, который я мог бы использовать? Я хочу прочитать очень сложный URL.
Портативная библиотека классов (PCL) Версия HttpUtility.ParseQueryString
Ответ 1
HttpUtility.ParseQueryString
возвращает HttpValueCollection
(Внутренний класс), который наследует от NameValueCollection
. NameValueCollection
представляет собой набор пар ключевых значений, таких как словарь, но он поддерживает дубликаты, поддерживает порядок и реализует только IEnumerable
(эта коллекция является предварительным генериком). NameValueCollection
не поддерживается в PCL.
Мое решение (частично снятое и измененное из платформы .NET) заключается в замене HttpValueCollection на Collection<HttpValue>
, где 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<HttpValue>
{
#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 теперь наследует от Collection, а не List, как выделено в комментариях.
ОБНОВЛЕНИЕ 2
Обновлен для использования WebUtility.UrlDecode при использовании .NET 4.5 благодаря @Paya.
Ответ 2
Вы также можете реализовать его следующим образом:
public static class HttpUtility
{
public static Dictionary<string, string> 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 test для этого:
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);
}
}
Ответ 3
My Flurl библиотека - это PCL, который анализирует строки запроса в IDictionary<string, object>
, когда вы создаете объект Url
из строки:
using Flurl;
var url = new Url("http://...");
// get values from url.QueryParams dictionary
Соответствующая логика синтаксического анализа здесь. Flurl невелик, но вы можете свободно прокручивать эти биты, если хотите.
Ответ 4
Сегодня я сделал пакет nuget, который выполняет базовое построение запросов и синтаксический анализ. Он предназначен для личного использования, но доступен с репозитария nuget.com. Для личного использования означает, что он не может полностью соответствовать "спецификациям HTTP-запросов". Ссылка Nuget здесь
Он основан на словаре, поэтому не поддерживает повторяющиеся ключи, главным образом потому, что я не знаю, почему вы хотели бы этого... (может кто-нибудь просветить меня?)
Он имеет 1 класс, представляющий запрос, который поддерживает добавление, получение параметров, проверку, содержит ли он ключ... И статический метод для синтаксического анализа ключа и возврата экземпляра запроса.