在.NET Core中解析和修改查询字符串


112

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

我宁愿不要&foo=bar使用或使用正则表达式,URI的转义很棘手。而是我想使用一个内置的机制,我知道它将正确执行此操作并处理转义。

发现 一个 的答案,所有使用HttpUtility。但是,由于这是ASP.NET Core,因此不再有System.Web程序集,因此也就没有了HttpUtility

针对核心运行时,在ASP.NET Core中执行此操作的合适方法是什么?


另一种Microsoft.AspNet.WebUtilties可能是Mono.HttpUtility图书馆
梅森2015年

我为此写了一篇文章,在这里看看:neelbhatt40.wordpress.com/2017/07/06/…–
Neel

2
2017年更新:.NET Core 2.0现在包括HttpUtilityParseQueryString方法。
KTCO

Answers:


152

如果使用的是ASP.NET Core 1或2,则可以Microsoft.AspNetCore.WebUtilities.QueryHelpersMicrosoft.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);

请注意,与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) 
}

您将获得key:value对的集合-像这样

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

1
仅供参考,WebUtilities软件包与.net core 1.0不兼容。您可能需要Microsoft.AspNetCore.WebUtilities代替。
Jaime

6
@Jaime很棒的观察!您可以使用该更新来编辑我的答案,以便为此获得信誉吗?
vcsjones'7

3
编辑完成。对于旧版.net版本,还要保留旧名称空间。
Jaime

1
看来使用QueryHelpers.AddQueryString将自动UrlEscape字符串-方便。
乔什(Josh)

2
现在的返回类型为IDictionary <string,StringValues>而不是IDictionary <string,string []>
btlog

35

只需几个简单的步骤即可完成采用绝对URI并仅使用ASP.NET Core程序包处理其查询字符串的最简单,最直观的方法:

安装套件

PM>安装包Microsoft.AspNetCore.WebUtilities
PM>安装包Microsoft.AspNetCore.Http.Extensions

重要班

只是指出它们,这是我们将要使用的两个重要类:QueryHelpersStringValuesQueryBuilder

代码

// 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/



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的帮助,已更改
Wagner Pereira

1
警告:由于字典已设置为<string,string>,因此如果查询中存在数组,则此操作将无效!(例如“?item = 1&item = 2”)解决方法:针对.net core 3.1使用IEnumerable <KeyValuePair <string,string >>或Dictionary <string,StringValues>
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+"");
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.