Розбирайте та змінюйте рядок запиту в .NET Core


113

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

Я вважаю за краще не &foo=barзастосовувати або використовувати регулярні вирази. Швидше я хочу використовувати вбудований механізм, який, я знаю, зробить це правильно і впорається з втечею.

Я знайшов в тонну відповідей , що будь-яке використання HttpUtility. Однак, будучи ASP.NET Core, більше немає збірки System.Web, отже, не більше HttpUtility.

Який підходящий спосіб зробити це в ASP.NET Core, орієнтуючись на час виконання ядра?


Альтернативою Microsoft.AspNet.WebUtiltiesможе бути Mono.HttpUtilityбібліотека .
муляр

Я написав пост для того ж, подивіться тут: neelbhatt40.wordpress.com/2017/07/06/…
Neel

2
Оновлення 2017: .NET Core 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 і не потребує окремої посилання на нукетний пакет.

Щоб розібрати його у словнику:

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, ]}

1
FYI, пакет WebUtilities не сумісний з .net core 1.0. Microsoft.AspNetCore.WebUtilitiesНатомість вам може знадобитися .
Хайме

6
@Jaime Дивовижне спостереження! Чи можете ви відредагувати мою відповідь цим оновленням, щоб отримати кредит за нього?
vcsjones

3
Видання виконано. Збереження також старого простору імен для випадку застарілих версій .net.
Хайме

1
Здається, що використання QueryHelpers.AddQueryString автоматично UrlEscape рядків - зручно.
Джош

2
Тип повернення тепер IDictionary <string, StringValues>, а не IDictionary <string, string []>
btlog

35

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

Встановити пакети

PM> Install-Package Microsoft.AspNetCore.WebUtilities
PM> Install-Package 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
Попередження: це не буде працювати, якщо в запиті є масиви, оскільки словник встановлений <string, string>! (наприклад, "? item = 1 & item = 2") Workaraound: використовуйте IEnumerable <KeyValuePair <string, string >> або словник <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


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

Наступні та попередні посилання, наприклад у поданні:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.