Разбор и изменение строки запроса в .NET Core

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

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

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

Каков подходящий способ сделать это в ASP.NET Core при ориентации на основное время выполнения?

Ответ 1

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

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

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

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

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

Если вы хотите добавить параметр в строку запроса, вы можете использовать другой метод в 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 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, ]}

Ответ 2

Самый простой и интуитивно понятный способ получить абсолютный 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/

Ответ 3

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 указывает на это также.

Ответ 4

Эта функция возвращает 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;
}

Ответ 5

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

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

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2

Ответ 6

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

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

Следующие и предыдущие ссылки, например, в виде:

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

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