Analizza e modifica una stringa di query in .NET Core


113

Mi viene fornito un URI assoluto che contiene una stringa di query. Sto cercando di aggiungere in modo sicuro un valore alla stringa di query e modificare un parametro esistente.

Preferirei non attaccare &foo=baro usare espressioni regolari, l'escape dell'URI è complicato. Piuttosto voglio usare un meccanismo integrato che so che lo farà correttamente e gestirà l'escape.

Ho trovato un sacco di risposte che usano tutti HttpUtility. Tuttavia, essendo ASP.NET Core, non c'è più l'assembly System.Web, quindi non più HttpUtility.

Qual è il modo appropriato per eseguire questa operazione in ASP.NET Core durante il targeting del runtime principale?


Un'alternativa Microsoft.AspNet.WebUtiltiespotrebbe essere la Mono.HttpUtilitylibreria .
muratore

Ho scritto un post per lo stesso, dai un'occhiata qui: neelbhatt40.wordpress.com/2017/07/06/…
Neel

2
Aggiornamento 2017: .NET Core 2.0 ora include HttpUtilitye ParseQueryStringmetodo.
KTCO

Risposte:


152

Se usi ASP.NET Core 1 o 2, puoi farlo con Microsoft.AspNetCore.WebUtilities.QueryHelpersil pacchetto Microsoft.AspNetCore.WebUtilities .

Se si usa ASP.NET Core 3.0 o versione successiva, WebUtilitiesora fa parte di ASP.NET SDK e non richiede un riferimento al pacchetto nuget separato.

Per analizzarlo in un dizionario:

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

Si noti che a differenza ParseQueryStringdi System.Web, restituisce un dizionario di tipo IDictionary<string, string[]>in ASP.NET Core 1.x o IDictionary<string, StringValues>ASP.NET Core 2.x o versione successiva, quindi il valore è una raccolta di stringhe. Questo è il modo in cui il dizionario gestisce più parametri della stringa di query con lo stesso nome.

Se desideri aggiungere un parametro alla stringa di query, puoi utilizzare un altro metodo su 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);

Utilizzando .net core 2.2 è possibile ottenere la stringa di query utilizzando

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

Otterrai una raccolta di coppie chiave: valore, come questa

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

1
Cordiali saluti, il pacchetto WebUtilities non è compatibile con .net core 1.0. Potrebbe essere necessario Microsoft.AspNetCore.WebUtilitiesinvece.
Jaime

6
@ Jaime Awesome osservazione! Puoi modificare la mia risposta con quell'aggiornamento in modo da ottenerne il merito?
vcsjones

3
Edizione fatta. Mantenendo anche il vecchio spazio dei nomi per le versioni legacy di .net.
Jaime

1
Sembra che usando QueryHelpers.AddQueryString UrlEscape automaticamente le stringhe - a portata di mano.
Josh

2
Il tipo restituito è ora IDictionary <string, StringValues> anziché IDictionary <string, string []>
btlog

35

Il modo più semplice e intuitivo per prendere un URI assoluto e manipolare la sua stringa di query utilizzando solo i pacchetti ASP.NET Core, può essere eseguito in pochi semplici passaggi:

Installa pacchetti

PM> Pacchetto di installazione Microsoft.AspNetCore.WebUtilities
PM> Pacchetto di installazione Microsoft.AspNetCore.Http.Extensions

Classi importanti

Solo per segnalarli, ecco le due classi importanti che useremo : QueryHelpers , StringValues , QueryBuilder .

Il codice

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

Per tenerti aggiornato su eventuali modifiche, puoi controllare il mio post sul blog su questo qui: http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/


17

HttpRequestha una Queryproprietà che espone la stringa di query analizzata tramite l' IReadableStringCollectioninterfaccia:

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

Anche questa discussione su GitHub lo punta.


10

Questa funzione restituisce Dictionary<string, string>e non viene utilizzata Microsoft.xxxper la compatibilità

Accetta la codifica dei parametri su entrambi i lati

Accetta chiavi duplicate (restituisce l'ultimo valore)

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

Funziona, ma dovresti aggiungere il controllo dell'indice prima di iniziare la sottostringa, poiché esiste la possibilità che quella riga non contenga "=". Che causa un'eccezione.
Taurib

1
Grazie @Taurib per l'aiuto, cambiato
Wagner Pereira

1
Attenzione: questo non funzionerà se ci sono array nella query poiché il dizionario è impostato su <stringa, stringa>! (ad esempio "? item = 1 & item = 2") Workaraound: usa IEnumerable <KeyValuePair <string, string >> o Dictionary <string, StringValues> per .net core 3.1
theCuriousOne

Grazie @theCuriousOne, in questa routine, restituisci l'ultimo valore, "Accetta chiavi duplicate (restituisci ultimo valore)" per semplicità, la tua soluzione è ok per restituire tutti i valori.
Wagner Pereira,

1

È importante notare che nel tempo trascorso da quando la risposta principale è stata contrassegnata come corretta, Microsoft.AspNetCore.WebUtilitiesè stato effettuato un aggiornamento della versione principale (da 1.xx a 2.xx).

Detto questo, se stai costruendo contro di netcoreapp1.1te dovrai eseguire quanto segue, che installa l'ultima versione supportata 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2


1

Lo uso come metodo di estensione, funziona con qualsiasi numero di parametri:

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

Collegamenti successivi e precedenti, ad esempio in una vista:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.