Is really QueryString case insensitive?

2019-05-07 05:48发布

问题:

I am working on a payment platform and, in response to a payment, a simple GET call, with some params in the query string, is made to my listener:

http://localhost/mytest/listener?TIMECREATED=04.08.2015+12%3A22%3A27&statoattuale=OK&PREVIOUSSTATE=IN&CURRENTSTATE=payment_approved&tipomessaggio=PAYMENT_STATE&DESCRIZIONE=CAMBIO+DI+STATO&datacreazione=04.08.2015+12%3A22%3A27&stabilimento=xxxxxx&MerchantNumber=xxxxxx&descrizione=CAMBIO+DI+STATO&OBJECT=PAYMENT&TIMEGENERATED=04.08.2015+12%3A23%3A17&MERCHANTNUMBER=xxxxxx&statoprecedente=IN&MERCHANTACCOUNT=xxxxxx&numeroOrdine=myOrderNo&numeroCommerciante=xxxxxx&datagenerazione=04.08.2015+12%3A23%3A17&ORDERNUMBER=myOrderNo&Stabilimento=xxxxxx&mac=CaWJiRCxbWH%2FsNFMvHUD2A%3D%3D&MAC=AnsEvRHkvMwRL%2FgehVtnhA%3D%3D

When I inspect Request.QueryString what I get is a mess of the param order and case. Seems like they are reordered with adjusted case for the first occurence. Like this:

TIMECREATED=04.08.2015 12:22:27&statoattuale=OK&PREVIOUSSTATE=IN&CURRENTSTATE=payment_approved&tipomessaggio=PAYMENT_STATE&DESCRIZIONE=CAMBIO DI STATO&DESCRIZIONE=CAMBIO DI STATO&datacreazione=04.08.2015 12:22:27&stabilimento=xxxxxx&stabilimento=xxxxxx&MerchantNumber=xxxxxx&MerchantNumber=xxxxxx&OBJECT=PAYMENT&TIMEGENERATED=04.08.2015 12:23:17&statoprecedente=IN&MERCHANTACCOUNT=999988801&numeroOrdine=myOrderNo&numeroCommerciante=xxxxxx&datagenerazione=04.08.2015 12:23:17&ORDERNUMBER=myOrderNo&mac=CaWJiRCxbWH/sNFMvHUD2A==&mac=AnsEvRHkvMwRL/gehVtnhA==

To me it looks like a bug, becasue the RFC3986 states:

When a URI uses components of the generic syntax, the component syntax equivalence rules always apply; namely, that the scheme and host are case-insensitive and therefore should be normalized to lowercase. For example, the URI is equivalent to http://www.example.com/. The other generic syntax components are assumed to be case-sensitive unless specifically defined otherwise by the scheme (see Section 6.2.3).

At the moment I solved my problem by manually parsing Url.Query, but I still do not think that how behave Request.QueryString is correct.

Can someone shed some light on the matter?

回答1:

Unfortunately, the API doesn't provide a way to make the Request.QueryString collection case sensitive (or the Request.Headers or Request.Form collections, for that matter).

However, with a bit of reverse engineering via reflection, it is not that difficult to do.

public class CaseSensitiveQueryStringCollection : System.Collections.Specialized.NameValueCollection
{
    public CaseSensitiveQueryStringCollection(string queryString, bool urlencoded, System.Text.Encoding encoding)
        // This makes it case sensitive, the default is StringComparer.OrdinalIgnoreCase
        : base(StringComparer.Ordinal)
    {
        if (queryString.StartsWith("?"))
        {
            queryString = queryString.Substring(1);
        }

        this.FillFromString(queryString, urlencoded, encoding);
    }

    internal void FillFromString(string s, bool urlencoded, System.Text.Encoding encoding)
    {
        int num = (s != null) ? s.Length : 0;
        for (int i = 0; i < num; i++)
        {
            int startIndex = i;
            int num4 = -1;
            while (i < num)
            {
                char ch = s[i];
                if (ch == '=')
                {
                    if (num4 < 0)
                    {
                        num4 = i;
                    }
                }
                else if (ch == '&')
                {
                    break;
                }
                i++;
            }
            string str = null;
            string str2 = null;
            if (num4 >= 0)
            {
                str = s.Substring(startIndex, num4 - startIndex);
                str2 = s.Substring(num4 + 1, (i - num4) - 1);
            }
            else
            {
                str2 = s.Substring(startIndex, i - startIndex);
            }
            if (urlencoded)
            {
                base.Add(HttpUtility.UrlDecode(str, encoding), HttpUtility.UrlDecode(str2, encoding));
            }
            else
            {
                base.Add(str, str2);
            }
            if ((i == (num - 1)) && (s[i] == '&'))
            {
                base.Add(null, string.Empty);
            }
        }
    }
}

Usage

var query = new CaseSensitiveQueryStringCollection(
    HttpContext.Current.Request.Url.Query, 
    true, 
    System.Text.Encoding.UTF8);

When you use a querystring like ?MAC=123&mac=456, you can see they are kept separate.