在.NETCore 中分析和修改查询字符串

我得到了一个包含查询字符串的绝对 URI。我希望安全地向查询字符串追加一个值,并更改一个现有参数。

我宁愿不附加在 &foo=bar,或使用正则表达式,URI 转义是棘手的。相反,我想使用一种内置的机制,我知道它能够正确地完成这项工作并处理转义。

我有 找到了 <一 href="https://st一ckoverflow.com/q/10232034/492405">一 的答案,都使用 HttpUtility。然而,这是 ASP.NET 核心,没有更多的系统。网络组装不再,因此没有更多的 HttpUtility

在 ASP.NET Core 中,针对核心运行时采用什么适当的方法来实现这一点?

105055 次浏览

如果您使用的是 ASP.NET Core 1或2,那么您可以在 微软。 AspNetCore。网络工具包中使用 Microsoft.AspNetCore.WebUtilities.QueryHelpers来实现这一点。

如果您使用的是 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不同。这将返回 ASP.NET Core 1.x 中类型为 IDictionary<string, string[]>的字典,或 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 core 2.2,您可以使用

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

您将得到一组 key: value 对,如下所示

[0] {[companyName, ]}
[1] {[shop, ]}
[2] {[breath, ]}
[3] {[hand, ]}
[4] {[eye, ]}
[5] {[firstAid, ]}
[6] {[eyeCleaner, ]}

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 上的讨论 也指出了这一点。

仅使用 ASP.NET Core 包获取绝对 URI 并操作其查询字符串的最简单和最直观的方法可以通过几个简单的步骤实现:

安装软件包

下午 > 安装-软件包微软。 AspNetCore。网络工具
PM > 安装-软件包 Microsoft. AspNetCore. Http. Extended

重要课程

只是为了指出它们,下面是我们将要使用的两个重要类: 查询帮助程序字符串值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/

值得注意的是,自从上面的答案被标记为正确以来,Microsoft.AspNetCore.WebUtilities已经有了一个主要的版本更新(从1.x.x 到2.x.x)。

也就是说,如果您正在构建基于 netcoreapp1.1的系统,那么您将需要运行以下命令,它将安装最新支持的版本 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2

这个函数返回 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;
}

我使用它作为扩展方法,适用于任意数量的参数:

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+"");

我不确定它是在什么时候加上去的,但是早在。NET Core 3.1 HttpUtility.ParseQueryString是可用的,并内置在标准中。NET Microsoft.NETCore.App框架。我能够从一个类库访问它,而不需要额外的 NuGet 包或特殊的 dll 引用。

我用.NET Core 3.1、 .NET 6和.NET 7测试过它。

这种方法的好处是,如果您正在构建一个可以在 ASP.NET 上下文之外使用的库,那么您不必引用任何会给您的项目增加膨胀的 Web 库。

当然,如果你在 ASP.NET Core 应用程序中需要它,Microsoft.AspNetCore.WebUtilities类是完全有效的。