В чем разница между Request.Url.Query и Request.QueryString? - PullRequest
13 голосов
/ 08 февраля 2010

Я обнаружил ошибку в приложении перезаписи URL. Ошибка обнаружилась как проблема кодирования некоторых диакритических символов в строке запроса.

По сути, проблема заключалась в том, что запрос, который в основном был /search.aspx?search=heřmánek, был переписан строкой запроса "search = he% c5% 99m% c3% a1nek"

Правильное значение (с использованием другого, рабочего кода) было переписать строку запроса как "search = he% u0159m% u00e1nek"

Обратите внимание на разницу между двумя строками. Однако, если вы разместите оба, вы увидите, что Url Encoding воспроизводит одну и ту же строку. Только когда вы используете функцию context.Rewrite, кодировка нарушается. Разорванная строка возвращает 'heÅmánek' (используя Request.QueryString ["Search"], а рабочая строка возвращает "heřmánek". Это изменение происходит после вызова функции перезаписи.

Я проследил это до одного набора кода с помощью Request.QueryString (работает), а другой с помощью Request.Url.Query (request.Url возвращает экземпляр Uri).

Пока я исправил ошибку, в моем понимании есть дыра, поэтому, если кто-то знает разницу, я готов к уроку.

Ответы [ 3 ]

3 голосов
/ 08 февраля 2010

То, что вы указали как «битая» закодированная строка, на самом деле является правильной кодировкой в ​​соответствии со стандартами. Тот, который вы указали как «правильную» кодировку, использует нестандартное расширение спецификаций, чтобы разрешить формат %uXXXX (я полагаю, что он должен указывать кодировку UTF-16).

В любом случае «разбитая» закодированная строка в порядке. Вы можете использовать следующий код для проверки:

Uri uri = new Uri("http://www.example.com/test.aspx?search=heřmánek");
Console.WriteLine(uri.Query);
Console.WriteLine(HttpUtility.UrlDecode(uri.Query));

Работает нормально. Однако ... догадываясь, я попробовал UrlDecode с указанной кодовой страницей Latin-1 вместо UTF-8 по умолчанию:

Console.WriteLine(HttpUtility.UrlDecode(uri.Query, 
           Encoding.GetEncoding("iso-8859-1")));

... и я получил плохое значение, которое вы указали, 'heÅmánek'. Другими словами, похоже, что вызов HttpContext.RewritePath() каким-то образом изменяет urlencoding / decoding для использования кодовой страницы Latin-1, а не UTF-8, которая является кодировкой по умолчанию, используемой методами UrlEncode / Decode.

Это похоже на ошибку, если вы спросите меня. Вы можете посмотреть на код RewritePath() в отражателе и увидеть, что он определенно играет со строкой запроса - передавая ее всем видам функций виртуального пути, а также некоторому неуправляемому коду IIS.

Интересно, будет ли где-нибудь в пути Uri в ядре объекта Request работать с неверной кодовой страницей? Это объясняет, почему Request.Querystring (который является просто необработанными значениями из заголовков HTTP) будет правильным, в то время как Uri, использующий неправильную кодировку для диакритических знаков, будет неправильным.

3 голосов
/ 08 февраля 2010

Ваш вопрос действительно вызвал у меня интерес, поэтому я немного почитал за прошедший час или около того. Я не совсем уверен, что нашел ответ, но выкину его, чтобы узнать, что вы думаете.

Из того, что я читал до сих пор, Request.QueryString на самом деле является "разобранной версией переменной QUERY_STRING в коллекции ServerVariables" [ссылка] , где как Request.Url (как вы указали) ) необработанный URL, инкапсулированный в объекте Uri. Согласно этой статье , конструктор класса Uri "... анализирует [строку url], помещает ее в канонический формат и делает все необходимые escape-кодировки".

Таким образом, представляется, что Request.QueryString использует другую функцию для анализа переменной «QUERY_STRING» из конструктора ServerVariables. Это объясняет, почему вы видите разницу между ними. Теперь, почему различные методы кодирования используются пользовательской функцией синтаксического анализа, а функция синтаксического анализа объекта Uri совершенно мне не известна. Может быть, кто-то более разбирающийся в DLL-библиотеке aspnet_isapi может дать ответы на этот вопрос.

В любом случае, надеюсь, мой пост имеет смысл. В дополнение к этому, я хотел бы добавить еще одну ссылку, которая также обеспечивала очень подробное и интересное чтение: http://download.microsoft.com/download/6/c/a/6ca715c5-2095-4eec-a56f-a5ee904a1387/Ch-12_HTTP_Request_Context.pdf

0 голосов
/ 01 апреля 2011

Я провел небольшое исследование за прошедший день или около того, и я думаю, что у меня есть некоторая информация об этом.

Когда вы используете Request.Querystring или HttpUtility.UrlDecode (или Encode), он использует кодировку, указанную в элементе (в частности, атрибуте requestEncoding) файла web.config (или иерархии .config, если вы этого не сделали). указано) --- НЕ Encoding.Default, который является кодировкой по умолчанию для вашего сервера.

Если для кодировки установлено значение UTF-8, один символ Unicode может быть закодирован как шестнадцатеричные значения 2% xx. Он также будет декодирован таким образом, когда передано все значение.

Если вы UrlDecoding с другой кодировкой, с которой не был закодирован URL-адрес, вы получите другой результат.

Поскольку HttpUtility.UrlEncode и UrlDecode могут принимать параметр кодирования, заманчиво попытаться кодировать с использованием кодовой страницы ANSI, но UTF-8 - правильный путь, если у вас есть поддержка браузера (очевидно, старые версии не поддерживают UTF-8). Вам просто нужно убедиться, что он правильно установлен, и обе стороны будут работать нормально.

UTF-8 Кажется кодировкой по умолчанию: (из .net отражатель System.Web.HttpRequest)

internal Encoding QueryStringEncoding
{
    get
    {
        Encoding contentEncoding = this.ContentEncoding;
        if (!contentEncoding.Equals(Encoding.Unicode))
        {
            return contentEncoding;
        }
        return Encoding.UTF8;
    }
}

Следуя пути, чтобы узнать this.ContentEncoding ведет вас (также в HttpRequest)

public Encoding ContentEncoding
{
    get
    {
        if (!this._flags[0x20] || (this._encoding == null))
        {
            this._encoding = this.GetEncodingFromHeaders();
            if (this._encoding == null)
            {
                GlobalizationSection globalization = RuntimeConfig.GetLKGConfig(this._context).Globalization;
                this._encoding = globalization.RequestEncoding;
            }
            this._flags.Set(0x20);
        }
        return this._encoding;
    }
    set
    {
        this._encoding = value;
        this._flags.Set(0x20);
    }
}

Чтобы ответить на ваш конкретный вопрос о разнице между Request.Url.Quer и Request.QueryString ... вот как HttpRequest создает свое свойство Url:

public Uri Url
{
    get
    {
        if ((this._url == null) && (this._wr != null))
        {
            string queryStringText = this.QueryStringText;
            if (!string.IsNullOrEmpty(queryStringText))
            {
                queryStringText = "?" + HttpEncoder.CollapsePercentUFromStringInternal(queryStringText, this.QueryStringEncoding);
            }
            if (AppSettings.UseHostHeaderForRequestUrl)
            {
                string knownRequestHeader = this._wr.GetKnownRequestHeader(0x1c);
                try
                {
                    if (!string.IsNullOrEmpty(knownRequestHeader))
                    {
                        this._url = new Uri(this._wr.GetProtocol() + "://" + knownRequestHeader + this.Path + queryStringText);
                    }
                }
                catch (UriFormatException)
                {
                }
            }
            if (this._url == null)
            {
                string serverName = this._wr.GetServerName();
                if ((serverName.IndexOf(':') >= 0) && (serverName[0] != '['))
                {
                    serverName = "[" + serverName + "]";
                }
                this._url = new Uri(this._wr.GetProtocol() + "://" + serverName + ":" + this._wr.GetLocalPortAsString() + this.Path + queryStringText);
            }
        }
        return this._url;
    }
}

Вы можете видеть, что он использует класс HttpEncoder для декодирования, но использует то же значение QueryStringEncoding.

Поскольку я уже выкладываю здесь много кода, и любой может получить .NET Reflector, я собираюсь описать остальные. Свойство QueryString происходит из коллекции HttpValueCollection, которая использует метод FillFromEncodedBytes для окончательного вызова HttpUtility.UrlDecode (со значением QueryStringEncoding, установленным выше), который в конечном итоге вызывает HttpEncoder для его декодирования. Похоже, они используют другую методологию для декодирования действительных байтов строки запроса, но кодировка, которую они используют для этого, кажется, одинакова.

Мне интересно, что HttpEncoder имеет так много функций, которые, кажется, делают одно и то же, поэтому возможно, что в этих методах есть различия, которые могут вызвать проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...