Portable Class Library (PCL) Version Of HttpUtility.ParseQueryString

你。 提交于 2019-12-09 08:35:11

问题


Is there a Portable Class Library (PCL) version Of HttpUtility.ParseQueryString contained in System.Web or some code I could use? I want to read a very complex URL.


回答1:


HttpUtility.ParseQueryString returns HttpValueCollection (Internal Class) which inherits from NameValueCollection. NameValueCollection is a collection of key value pairs like a dictionary but it supports duplicates, maintains order and only implements IEnumerable (This collection is pre-generics). NameValueCollection is not supported in PCL.

My solution (Partly lifted and modified from the .NET framework) is to substitute HttpValueCollection with Collection<HttpValue> where HttpValue is just a key value pair.

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

Updated so that HttpValueCollection now inherits from Collection rather than List as highlighted in the comments.

UPDATE 2

Updated to use WebUtility.UrlDecode if using .NET 4.5, thanks to @Paya.




回答2:


You can also implement it like this:

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

Here is a Unit test for that:

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 library is a PCL that parses query strings into IDictionary<string, object> when you instantiate a Url object from a string:

using Flurl;

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

The relevant parsing logic is here. Flurl is small, but feel free to swipe just those bits if you want.




回答4:


I made a nuget package today that does basic query building and parsing. It's made for personal usage but available from the nuget.com repo. For personal usage means it may not be fully compliant with the 'http query specs'. Nuget link here

It's based on a dictionary so doesn't support duplicate keys, mainly because I don't know why you would want that... (can anyone enlighten me?)

It has 1 class representing a query that supports adding, getting parameters, checking if it contains a key... And a static method to parse a key and return a query instance.



来源:https://stackoverflow.com/questions/20268544/portable-class-library-pcl-version-of-httputility-parsequerystring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!