Анализировать и изменять строку запроса в .NET Core

113

Мне предоставляется абсолютный URI, содержащий строку запроса. Я хочу безопасно добавить значение в строку запроса и изменить существующий параметр.

Я бы предпочел не &foo=barиспользовать регулярные выражения, экранирование URI - сложная задача. Скорее я хочу использовать встроенный механизм, который, как я знаю, будет делать это правильно и обрабатывать экранирование.

Я нашел в тонну ответов , что любое использование HttpUtility. Однако это ASP.NET Core, поэтому сборки System.Web больше нет, а значит, больше нет HttpUtility.

Как лучше всего сделать это в ASP.NET Core, ориентируясь на базовую среду выполнения?

vcsjones
источник
Альтернативой Microsoft.AspNet.WebUtiltiesможет быть Mono.HttpUtilityбиблиотека .
Мейсон
Я написал для того же поста, посмотрите здесь: neelbhatt40.wordpress.com/2017/07/06/…
Neel
2
Обновление 2017: .NET Ядро 2.0 теперь включает в себя HttpUtilityи ParseQueryStringметод.
KTCO

Ответы:

152

Если вы используете ASP.NET Core 1 или 2, это можно сделать с Microsoft.AspNetCore.WebUtilities.QueryHelpersпомощью Microsoft.AspNetCore.WebUtilities. пакета .

Если вы используете ASP.NET Core 3,0 или более WebUtilitiesпоздней версии , теперь он является частью пакета SDK ASP.NET и не требует отдельной ссылки на пакет nuget.

Чтобы разобрать его в словаре:

var uri = new Uri(context.RedirectUri);
var queryDictionary = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query);

Обратите внимание, что в отличие от ParseQueryStringSystem.Web, это возвращает словарь типа IDictionary<string, string[]>в ASP.NET Core 1.x или IDictionary<string, StringValues>в ASP.NET Core 2.x или выше, поэтому значение представляет собой набор строк. Вот так словарь обрабатывает несколько параметров строки запроса с одинаковым именем.

Если вы хотите добавить параметр в строку запроса, вы можете использовать другой метод для QueryHelpers:

var parametersToAdd = new System.Collections.Generic.Dictionary<string, string> { { "resource", "foo" } };
var someUrl = "http://www.google.com";
var newUri = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(someUrl, parametersToAdd);

Используя .net core 2.2, вы можете получить строку запроса, используя

var request = HttpContext.Request;
var query = request.query;
foreach (var item in query){
   Debug.WriteLine(item) 
}

Вы получите набор пар ключ: значение - вот так

[0] {[companyName, ]}
[1] {[shop, ]}
[2] {[breath, ]}
[3] {[hand, ]}
[4] {[eye, ]}
[5] {[firstAid, ]}
[6] {[eyeCleaner, ]}
vcsjones
источник
1
К вашему сведению, пакет WebUtilities несовместим с .net core 1.0. Вам может понадобиться Microsoft.AspNetCore.WebUtilitiesвзамен.
Jaime
6
@Jaime Замечательное наблюдение! Можете ли вы отредактировать мой ответ с этим обновлением, чтобы получить за него кредит?
vcsjones 01
3
Издание сделано. Сохранение также старого пространства имен для устаревших версий .net.
Jaime
1
Похоже, что использование QueryHelpers.AddQueryString автоматически UrlEscape строк - удобно.
Джош
2
Тип возвращаемого значения теперь IDictionary <string, StringValues>, а не IDictionary <string, string []>
btlog
35

Самый простой и интуитивно понятный способ получить абсолютный URI и управлять его строкой запроса, используя только пакеты ASP.NET Core, можно выполнить в несколько простых шагов:

Установить пакеты

PM> Установочный пакет Microsoft.AspNetCore.WebUtilities
PM> Установочный пакет Microsoft.AspNetCore.Http.Extensions

Важные классы

Чтобы указать на них, вот два важных класса, которые мы будем использовать: QueryHelpers , StringValues , QueryBuilder. .

Код

// Raw URI including query string with multiple parameters
var rawurl = "https://bencull.com/some/path?key1=val1&key2=val2&key2=valdouble&key3=";

// Parse URI, and grab everything except the query string.
var uri = new Uri(rawurl);
var baseUri = uri.GetComponents(UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path, UriFormat.UriEscaped);

// Grab just the query string part
var query = QueryHelpers.ParseQuery(uri.Query);

// Convert the StringValues into a list of KeyValue Pairs to make it easier to manipulate
var items = query.SelectMany(x => x.Value, (col, value) => new KeyValuePair<string, string>(col.Key, value)).ToList();

// At this point you can remove items if you want
items.RemoveAll(x => x.Key == "key3"); // Remove all values for key
items.RemoveAll(x => x.Key == "key2" && x.Value == "val2"); // Remove specific value for key

// Use the QueryBuilder to add in new items in a safe way (handles multiples and empty values)
var qb = new QueryBuilder(items);
qb.Add("nonce", "testingnonce");
qb.Add("payerId", "pyr_");

// Reconstruct the original URL with new query string
var fullUri = baseUri + qb.ToQueryString();

Чтобы быть в курсе любых изменений, вы можете проверить мое сообщение в блоге об этом здесь: http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/

Бен Калл
источник
17

HttpRequestимеет Queryсвойство, которое предоставляет проанализированную строку запроса через IReadableStringCollectionинтерфейс:

/// <summary>
/// Gets the query value collection parsed from owin.RequestQueryString.
/// </summary>
/// <returns>The query value collection parsed from owin.RequestQueryString.</returns>
public abstract IReadableStringCollection Query { get; }

Это обсуждение на GitHub также указывает на это.

Юваль Ицчаков
источник
10

Эта функция возвращает Dictionary<string, string>и не используется Microsoft.xxxдля совместимости

Принимает кодировку параметров с обеих сторон

Принимает повторяющиеся ключи (возвращает последнее значение)

var rawurl = "https://emp.com/some/path?key1.name=a%20line%20with%3D&key2=val2&key2=valdouble&key3=&key%204=44#book1";
var uri = new Uri(rawurl);
Dictionary<string, string> queryString = ParseQueryString(uri.Query);

// queryString return:
// key1.name, a line with=
// key2, valdouble
// key3, 
// key 4, 44

public Dictionary<string, string> ParseQueryString(string requestQueryString)
{
    Dictionary<string, string> rc = new Dictionary<string, string>();
    string[] ar1 = requestQueryString.Split(new char[] { '&', '?' });
    foreach (string row in ar1)
    {
        if (string.IsNullOrEmpty(row)) continue;
        int index = row.IndexOf('=');
        if (index < 0) continue;
        rc[Uri.UnescapeDataString(row.Substring(0, index))] = Uri.UnescapeDataString(row.Substring(index + 1)); // use Unescape only parts          
     }
     return rc;
}
Вагнер Перейра
источник
Это работает, но вы должны добавить проверку индекса перед началом подстроки, поскольку существует вероятность, что эта строка не содержит '='. Что вызывает исключение.
Тауриб
1
Спасибо @Taurib за помощь, изменилось
Вагнер Перейра
1
Предупреждение: это не сработает, если в запросе есть массивы, поскольку словарь установлен <строка, строка>! (например, «? item = 1 & item = 2») Временное решение: используйте IEnumerable <KeyValuePair <string, string >> или Dictionary <string, StringValues> для .net core 3.1
theCuriousOne
Спасибо @theCuriousOne, в этой подпрограмме для простоты вернуть последнее значение «Принимает повторяющиеся ключи (вернуть последнее значение)», ваше решение может возвращать все значения.
Вагнер Перейра,
1

Важно отметить, что с тех пор, как верхний ответ был отмечен как правильный, Microsoft.AspNetCore.WebUtilities прошедшее с было выполнено обновление основной версии (с 1.xx до 2.xx).

Тем не менее, если вы строите против, netcoreapp1.1вам нужно будет запустить следующее, которое устанавливает последнюю поддерживаемую версию 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2

pimbrouwers
источник
1

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

public static string AddOrReplaceQueryParameter(this HttpContext c, params string[] nameValues)
    {
        if (nameValues.Length%2!=0)
        {
            throw new Exception("nameValues: has more parameters then values or more values then parameters");
        }
        var qps = new Dictionary<string, StringValues>();
        for (int i = 0; i < nameValues.Length; i+=2)
        {
            qps.Add(nameValues[i], nameValues[i + 1]);
        }
        return c.AddOrReplaceQueryParameters(qps);
    }

public static string AddOrReplaceQueryParameters(this HttpContext c, Dictionary<string,StringValues> pvs)
    {
        var request = c.Request;
        UriBuilder uriBuilder = new UriBuilder
        {
            Scheme = request.Scheme,
            Host = request.Host.Host,
            Port = request.Host.Port ?? 0,
            Path = request.Path.ToString(),
            Query = request.QueryString.ToString()
        };

        var queryParams = QueryHelpers.ParseQuery(uriBuilder.Query);

        foreach (var (p,v) in pvs)
        {
            queryParams.Remove(p);
            queryParams.Add(p, v);
        }

        uriBuilder.Query = "";
        var allQPs = queryParams.ToDictionary(k => k.Key, k => k.Value.ToString());
        var url = QueryHelpers.AddQueryString(uriBuilder.ToString(),allQPs);

        return url;
    }

Ссылки Next и Previous, например, в представлении:

var next = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex+1+"");

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
Габриэль Лука
источник