Przeanalizuj i zmodyfikuj ciąg zapytania w .NET Core

113

Otrzymuję bezwzględny identyfikator URI zawierający ciąg zapytania. Chcę bezpiecznie dołączyć wartość do ciągu zapytania i zmienić istniejący parametr.

Wolałbym nie dołączać &foo=barani nie używać wyrażeń regularnych, ucieczka URI jest trudna. Raczej chcę użyć wbudowanego mechanizmu, o którym wiem, że zrobi to poprawnie i poradzi sobie z ucieczką.

Mam znaleźć się mnóstwo odpowiedzi, że wszyscy użytku HttpUtility. Jednak to jest ASP.NET Core, nie ma już zestawu System.Web, więc nie ma więcej HttpUtility.

Jaki jest odpowiedni sposób na zrobienie tego w ASP.NET Core podczas określania podstawowego środowiska uruchomieniowego?

vcsjones
źródło
Alternatywą Microsoft.AspNet.WebUtiltiesmoże być Mono.HttpUtilitybiblioteka .
murarz
Napisałem post na to samo, zajrzyj tutaj: neelbhatt40.wordpress.com/2017/07/06/…
Neel
2
Aktualizacja 2017: .NET Core 2.0 zawiera teraz metodę HttpUtilityi ParseQueryString.
KTCO

Odpowiedzi:

152

Jeśli używasz ASP.NET Core 1 lub 2, możesz to zrobić Microsoft.AspNetCore.WebUtilities.QueryHelpersw pakiecie Microsoft.AspNetCore.WebUtilities .

Jeśli używasz ASP.NET Core 3,0 lub nowszego, WebUtilitiesjest teraz częścią zestawu ASP.NET SDK i nie wymaga oddzielnego odwołania do pakietu NuGet.

Aby przeanalizować go w słowniku:

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

Należy pamiętać, że w przeciwieństwie ParseQueryStringdo System.Web zwraca słownik typu IDictionary<string, string[]>w ASP.NET Core 1.x lub IDictionary<string, StringValues>w ASP.NET Core 2.x lub nowszym, więc wartość jest zbiorem ciągów. W ten sposób słownik obsługuje wiele parametrów ciągu zapytania o tej samej nazwie.

Jeśli chcesz dodać parametr do ciągu zapytania, możesz użyć innej metody na 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);

Używając .net core 2.2 możesz uzyskać ciąg zapytania używając

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

Otrzymasz zbiór par klucz: wartość - w ten sposób

[0] {[companyName, ]}
[1] {[shop, ]}
[2] {[breath, ]}
[3] {[hand, ]}
[4] {[eye, ]}
[5] {[firstAid, ]}
[6] {[eyeCleaner, ]}
vcsjones
źródło
1
FYI, pakiet WebUtilities nie jest zgodny z .net core 1.0. Zamiast tego możesz potrzebować Microsoft.AspNetCore.WebUtilities.
Jaime
6
@Jaime Niesamowita obserwacja! Czy możesz edytować moją odpowiedź za pomocą tej aktualizacji, aby otrzymać za nią uznanie?
vcsjones
3
Edycja wykonana. Zachowanie również starej przestrzeni nazw dla starszych wersji .net.
Jaime
1
Wygląda na to, że użycie QueryHelpers.AddQueryString automatycznie UrlEscape ciągów - przydatne.
Josh,
2
Typ zwracany to teraz IDictionary <string, StringValues>, a nie IDictionary <string, string []>
btlog
35

Najłatwiejszy i najbardziej intuicyjny sposób na pobranie bezwzględnego identyfikatora URI i manipulowanie jego ciągiem zapytania przy użyciu tylko pakietów ASP.NET Core można wykonać w kilku prostych krokach:

Zainstaluj pakiety

PM> Install-Package Microsoft.AspNetCore.WebUtilities
PM> Install-Package Microsoft.AspNetCore.Http.Extensions

Ważne zajęcia

Wystarczy wskazać je, tu są dwa ważne zajęcia będziemy używać: QueryHelpers , StringValues , QueryBuilder .

Kod

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

Aby być na bieżąco z wszelkimi zmianami, możesz sprawdzić mój wpis na blogu na ten temat tutaj: http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/

Ben Cull
źródło
17

HttpRequestma Querywłaściwość, która ujawnia przeanalizowany ciąg zapytania za pośrednictwem IReadableStringCollectioninterfejsu:

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

Ta dyskusja na GitHub również na to wskazuje.

Yuval Itzchakov
źródło
10

Ta funkcja zwraca Dictionary<string, string>i nie jest używana w Microsoft.xxxcelu zapewnienia zgodności

Akceptuje kodowanie parametrów po obu stronach

Akceptuje zduplikowane klucze (zwraca ostatnią wartość)

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;
}
Wagner Pereira
źródło
To działa, ale powinieneś dodać sprawdzanie indeksu przed rozpoczęciem podłańcucha, ponieważ istnieje możliwość, że ten wiersz nie zawiera '='. Co powoduje wyjątek.
Taurib
1
Dzięki @Taurib za pomoc, zmieniony
Wagner Pereira
1
Ostrzeżenie: to nie zadziała, jeśli w zapytaniu znajdują się tablice, ponieważ słownik jest ustawiony na <string, string>! (na przykład „? item = 1 & item = 2”). Obejście problemu: użyj IEnumerable <KeyValuePair <string, string >> lub Dictionary <string, StringValues> dla .net core 3.1
theCuriousOne
Dzięki @theCuriousOne, w tej procedurze zwróć ostatnią wartość „Akceptuje zduplikowane klucze (zwróć ostatnią wartość)” dla uproszczenia. Twoje rozwiązanie jest w porządku, aby zwrócić wszystkie wartości.
Wagner Pereira
1

Należy zauważyć, że od czasu, kiedy pierwsza odpowiedź została oznaczona jako prawidłowa, Microsoft.AspNetCore.WebUtilitiesnastąpiła główna aktualizacja wersji (z 1.xx do 2.xx).

To powiedziawszy, jeśli tworzysz przeciwko netcoreapp1.1, będziesz musiał uruchomić następujące oprogramowanie, które zainstaluje najnowszą obsługiwaną wersję 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2

pimbrouwers
źródło
1

Używam tego jako metody rozszerzającej, działa z dowolną liczbą parametrów:

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

Następny i poprzedni link, na przykład w widoku:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
Gabriel Luca
źródło