c# asp.net .net portable-class-library system.web

c# - Versión de la biblioteca de clases portátil(PCL) de HttpUtility.ParseQueryString



asp.net .net (4)

¿Existe una versión de Portable Class Library (PCL) de HttpUtility.ParseQueryString contenida en System.Web o algún código que pueda usar? Quiero leer una URL muy compleja.


Hice un paquete nuget hoy que hace la construcción básica de consultas y el análisis. Está hecho para uso personal pero está disponible en el repositorio de nuget.com. Para uso personal significa que puede no ser totalmente compatible con las ''especificaciones de consulta http''. Nuget enlace aquí

Se basa en un diccionario, por lo que no admite claves duplicadas, principalmente porque no sé por qué querría eso ... (¿alguien puede iluminarme?)

Tiene 1 clase que representa una consulta que admite agregar, obtener parámetros, verificar si contiene una clave ... Y un método estático para analizar una clave y devolver una instancia de consulta.


Mi biblioteca Flurl es una PCL que analiza las cadenas de consulta en IDictionary<string, object> cuando crea una instancia de un objeto Url desde una cadena:

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

La lógica de análisis relevante está here . Flurl es pequeño, pero siéntete libre de deslizar solo esos bits si quieres.


También puedes implementarlo así:

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])); } }

Aquí hay una prueba de unidad para eso:

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); } }


HttpUtility.ParseQueryString devuelve HttpValueCollection (Clase interna) que hereda de NameValueCollection . NameValueCollection es una colección de pares de valores clave como un diccionario pero admite duplicados, mantiene el orden y solo implementa IEnumerable (esta colección es pre-generica). NameValueCollection no es compatible con PCL.

Mi solución (parcialmente levantada y modificada del marco .NET) es sustituir HttpValueCollection con Collection<HttpValue> donde HttpValue es solo un par de valores clave.

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 }

ACTUALIZAR

Se actualizó para que HttpValueCollection ahora se hereda de la Colección en lugar de la Lista como se resalta en los comentarios

ACTUALIZACIÓN 2

Actualizado para usar WebUtility.UrlDecode si usa .NET 4.5, gracias a @Paya.