.NET Coreでクエリ文字列を解析して変更する


112

クエリ文字列を含む絶対URIが与えられます。クエリ文字列に値を安全に追加し、既存のパラメーターを変更しようとしています。

私はにタックし&foo=barたり、正規表現を使用したりしたくないので、URIエスケープはトリッキーです。むしろ、これを正しく実行してエスケープを処理することがわかっている組み込みのメカニズムを使用したいと思います。

私がしまし トン、すべての使用その答えのを。ただし、これはASP.NETコアであるため、System.Webアセンブリはもうありません。 HttpUtilityHttpUtility

コアランタイムをターゲットにしながらASP.NET Coreでこれを行う適切な方法は何ですか?


の代わりにライブラリを使用Microsoft.AspNet.WebUtiltiesできます。Mono.HttpUtility
メイソン

私はここで見て、同じのために記事を書いた:neelbhatt40.wordpress.com/2017/07/06/...
ニール

2
2017年更新:.NET Core 2.0にメソッドHttpUtilityParseQueryStringメソッドが含まれるようになりました。
KTCO

回答:


152

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

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
参考までに、WebUtilitiesパッケージは.net core 1.0と互換性がありません。Microsoft.AspNetCore.WebUtilities代わりに必要になる場合があります。
ハイメ、

6
@ハイメ素晴らしい観察!その更新で私の回答を編集して、クレジットを取得できますか?
vcsjones

3
エディションが完了しました。レガシー.netバージョンの場合のために、古いネームスペースも保持します。
Jaime

1
QueryHelpers.AddQueryStringを使用すると、文字列が自動的にUrlEscapeされるようです-便利です。
Josh、

2
戻り値の型は、IDictionary <string、string []>ではなく、IDictionary <string、StringValues>になりました
btlog

35

絶対URIを取得し、ASP.NET Coreパッケージのみを使用してそのクエリ文字列を操作する最も簡単で直感的な方法は、いくつかの簡単な手順で実行できます。

パッケージのインストール

PM> Install-Package Microsoft.AspNetCore.WebUtilities
PM> Install-Package 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/で私のブログ投稿をチェックしてください。


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

これは機能しますが、その行に「=」が含まれていない可能性があるため、部分文字列を開始する前にインデックスチェックを追加する必要があります。例外が発生します。
Taurib

1
助けてくれて@Tauribに感謝、変更
Wagner Pereira

1
警告:辞書が<string、string>に設定されているため、クエリに配列がある場合、これは機能しません。(たとえば、「?item = 1&item = 2」)Workaraound:IEnumerable <KeyValuePair <string、string >>またはDictionary <string、StringValues>を.netコア3.1に使用
theCuriousOne

@theCuriousOneに感謝します。このルーチンでは、単純化のために最後の値「重複したキーを受け入れる(最後の値を返す)」を返します。ソリューションはすべての値を返しても問題ありません。
Wagner Pereira

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+"");
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.