Analysieren und ändern Sie eine Abfragezeichenfolge in .NET Core


112

Ich erhalte einen absoluten URI, der eine Abfragezeichenfolge enthält. Ich möchte einen Wert sicher an die Abfragezeichenfolge anhängen und einen vorhandenen Parameter ändern.

Ich würde es vorziehen, nicht anzuheften &foo=baroder reguläre Ausdrücke zu verwenden, URI-Escape ist schwierig. Vielmehr möchte ich einen eingebauten Mechanismus verwenden, von dem ich weiß, dass er dies korrekt ausführt und das Entkommen handhabt.

Ich habe gefunden , eine Tonne von Antworten , die jede Nutzung HttpUtility. Da es sich jedoch um ASP.NET Core handelt, gibt es keine System.Web-Assembly mehr, also keine mehr HttpUtility.

Was ist der geeignete Weg, um dies in ASP.NET Core zu tun, während Sie auf die Core-Laufzeit abzielen?


Eine Alternative dazu Microsoft.AspNet.WebUtiltieskann die Mono.HttpUtilityBibliothek sein .
Maurer

Ich habe einen Beitrag für das gleiche geschrieben, schauen Sie hier: neelbhatt40.wordpress.com/2017/07/06/…
Neel

2
Update 2017: .NET Core 2.0 enthält jetzt HttpUtilityund ParseQueryStringVerfahren.
KTCO

Antworten:


152

Wenn Sie ASP.NET Core 1 oder 2 verwenden, können Sie dies Microsoft.AspNetCore.WebUtilities.QueryHelpersin den Microsoft.AspNetCore.WebUtilities tun Paket .

Wenn Sie ASP.NET Core 3.0 oder höher verwenden, WebUtilities ist es jetzt Teil des ASP.NET SDK und erfordert keine separate Nuget-Paketreferenz.

So analysieren Sie es in ein Wörterbuch:

var uri = new Uri(context.RedirectUri);
var queryDictionary = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query);

Beachten Sie, dass im Gegensatz ParseQueryStringzu System.Web ein Wörterbuch vom Typ IDictionary<string, string[]>in ASP.NET Core 1.x oder zurückgegeben wirdIDictionary<string, StringValues> in ASP.NET Core 2.x oder höher zurückgegeben wird, sodass der Wert eine Sammlung von Zeichenfolgen ist. Auf diese Weise verarbeitet das Wörterbuch mehrere Abfragezeichenfolgenparameter mit demselben Namen.

Wenn Sie der Abfragezeichenfolge einen Parameter hinzufügen möchten, können Sie eine andere Methode verwenden für 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);

Mit .net Core 2.2 können Sie die Abfragezeichenfolge mit abrufen

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

Sie erhalten eine Sammlung von Schlüssel: Wert-Paaren - so

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

1
Zu Ihrer Information, das WebUtilities-Paket ist nicht mit .net Core 1.0 kompatibel. Möglicherweise benötigen Sie Microsoft.AspNetCore.WebUtilitiesstattdessen.
Jaime

6
@ Jaime Tolle Beobachtung! Können Sie meine Antwort mit diesem Update bearbeiten, damit Sie dafür eine Gutschrift erhalten?
vcsjones

3
Edition fertig. Behalten Sie auch den alten Namespace für ältere .net-Versionen bei.
Jaime

1
Es scheint, dass die Verwendung von QueryHelpers.AddQueryString die Zeichenfolgen automatisch mit UrlEscape erstellt - praktisch.
Josh

2
Der Rückgabetyp ist jetzt IDictionary <string, StringValues> und nicht IDictionary <string, string []>
btlog

35

Der einfachste und intuitivste Weg, einen absoluten URI zu verwenden und seine Abfragezeichenfolge nur mit ASP.NET Core-Paketen zu bearbeiten, kann in wenigen einfachen Schritten erfolgen:

Installieren Sie Pakete

PM> Installationspaket Microsoft.AspNetCore.WebUtilities
PM> Installationspaket Microsoft.AspNetCore.Http.Extensions

Wichtige Klassen

Um sie darauf hinzuweisen, hier sind die beiden wichtigen Klassen, die wir verwenden werden: QueryHelpers , StringValues , QueryBuilder .

Der Code

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

Um über Änderungen auf dem Laufenden zu bleiben, können Sie meinen Blog-Beitrag dazu hier lesen : http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/


17

HttpRequesthat eine QueryEigenschaft, die die analysierte Abfragezeichenfolge über die IReadableStringCollectionSchnittstelle verfügbar macht:

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

Diese Diskussion auf GitHub weist ebenfalls darauf hin.


10

Diese Funktion wird zurückgegeben Dictionary<string, string>und Microsoft.xxxaus Kompatibilitätsgründen nicht verwendet

Akzeptiert die Parametercodierung auf beiden Seiten

Akzeptiert doppelte Schlüssel (letzten Wert zurückgeben)

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

Dies funktioniert, aber Sie sollten eine Indexprüfung hinzufügen, bevor Sie mit der Teilzeichenfolge beginnen, da die Möglichkeit besteht, dass diese Zeile kein '=' enthält. Welches verursacht Ausnahme.
Taurib

1
Danke @Taurib für die Hilfe, geändert
Wagner Pereira

1
Warnung: Dies funktioniert nicht, wenn die Abfrage Arrays enthält, da das Wörterbuch auf <string, string> gesetzt ist! (zum Beispiel "? item = 1 & item = 2") Problemumgehung: Verwenden Sie IEnumerable <KeyValuePair <Zeichenfolge, Zeichenfolge >> oder Dictionary <Zeichenfolge, ZeichenfolgeWerte> für .net Core 3.1
theCuriousOne

Dank @theCuriousOne geben Sie in dieser Routine der Einfachheit halber den letzten Wert "Akzeptiert doppelte Schlüssel (letzten Wert zurückgeben)" zurück. Ihre Lösung ist in Ordnung, alle Werte zurückzugeben.
Wagner Pereira

1

Es ist wichtig zu beachten, dass in der Zeit, seit die Top-Antwort als richtig markiert wurde, Microsoft.AspNetCore.WebUtilitiesein Hauptversionsupdate (von 1.xx auf 2.xx) durchgeführt wurde.

Das heißt, wenn Sie dagegen bauen, müssen netcoreapp1.1Sie Folgendes ausführen, wodurch die neueste unterstützte Version installiert wird 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2


1

Ich benutze dies als Erweiterungsmethode, arbeitet mit einer beliebigen Anzahl von Parametern:

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

Nächste und vorherige Links zum Beispiel in einer Ansicht:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.