web-dev-qa-db-fra.com

Analyser et modifier une chaîne de requête dans .NET Core

On me donne un URI absolu contenant une chaîne de requête. Je cherche à ajouter en toute sécurité une valeur à la chaîne de requête et à modifier un paramètre existant.

Je préférerais ne pas coller sur &foo=bar, Ou utiliser des expressions régulières, l'échappement d'URI est délicat. Je veux plutôt utiliser un mécanisme intégré qui, je le sais, le fera correctement et gérera l'échappement.

J'ai trouvéaton de réponses qui utilisent toutes HttpUtility. Cependant, étant ASP.NET Core, il n'y a plus d'assembly System.Web, donc plus de HttpUtility.

Quelle est la manière appropriée de le faire dans ASP.NET Core tout en ciblant le moteur d'exécution?

92
vcsjones

Si vous utilisez ASP.NET Core 1 ou 2, vous pouvez le faire avec Microsoft.AspNetCore.WebUtilities.QueryHelpers dans le package Microsoft.AspNetCore.WebUtilities .

Si vous utilisez ASP.NET Core 3.0 ou version ultérieure, WebUtilities fait maintenant partie du SDK ASP.NET et ne nécessite pas de référence de package de nuget distinct.

Pour l'analyser dans un dictionnaire:

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

Notez que contrairement à ParseQueryString dans System.Web, cela renvoie un dictionnaire de type IDictionary<string, string[]> dans ASP.NET Core 1.x ou IDictionary<string, StringValues> dans ASP.NET Core 2.x ou supérieur, la valeur est donc un ensemble de chaînes. Voici comment le dictionnaire gère plusieurs paramètres de chaîne de requête portant le même nom.

Si vous souhaitez ajouter un paramètre à la chaîne de requête, vous pouvez utiliser une autre méthode sur 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);

En utilisant .net core 2.2, vous pouvez obtenir la chaîne de requête en utilisant

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

Vous obtiendrez une collection de paires clé: valeur - comme ceci

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

Le moyen le plus simple et le plus intuitif de prendre un URI absolu et de manipuler sa chaîne de requête en utilisant uniquement les packages ASP.NET Core peut être effectué en quelques étapes simples:

Installer des paquets

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

Classes Importantes

Juste pour les signaler, voici les deux classes importantes que nous allons utiliser: QueryHelpers , StringValues , QueryBuilder .

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

Pour vous tenir au courant de tout changement, vous pouvez consulter mon article de blog à ce sujet ici: http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/

26
Ben Cull

HttpRequest a une propriété Query qui expose la chaîne de requête analysée via l'interface 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; }

Cette discussion sur GitHub l'indique également.

17
Yuval Itzchakov

Cette fonction retourne Dictionary<string, string> et n'utilise pas Microsoft.xxx pour la compatibilité

Accepte le codage des paramètres des deux côtés

Accepte les clés en double (retourne la dernière valeur)

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;
}
9
Wagner Pereira

Il est important de noter que depuis que la première réponse a été marquée comme correcte, Microsoft.AspNetCore.WebUtilities a eu une mise à jour majeure (de 1.x.x à 2.x.x).

Cela dit, si vous construisez contre netcoreapp1.1 vous devrez exécuter ce qui suit, qui installe la dernière version prise en charge 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2

1
pim

J'utilise ceci comme méthode d'extension, fonctionne avec un nombre quelconque de paramètres:

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

Liens suivant et précédent, par exemple dans une vue:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
1
Gabriel Luca