Разбор и изменение строки запроса в .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+"");