web-dev-qa-db-fra.com

NameValueCollection to URL Query?

Je sais que je peux le faire

var nv = HttpUtility.ParseQueryString(req.RawUrl);

Mais y a-t-il un moyen de le reconvertir en une URL?

var newUrl = HttpUtility.Something("/page", nv);
63
user34537

Si vous appelez simplement ToString() sur NameValueCollection, les paires nom-valeur seront renvoyées au format name1=value1&name2=value2 prêt pour la chaîne de requête. Notez que les types NameValueCollection ne prennent pas cela en charge et il est trompeur de suggérer ceci, mais le comportement fonctionne ici en raison du type interne renvoyé, comme expliqué ci-dessous.

Merci à @mjwills pour avoir signalé que la méthode HttpUtility.ParseQueryString renvoie en réalité un objet HttpValueCollection interne plutôt qu’un NameValueCollection normal ( malgré la documentation spécifiant NameValueCollection ). HttpValueCollection encode automatiquement la chaîne de requête lors de l'utilisation de ToString(). Il n'est donc pas nécessaire d'écrire une routine qui parcourt la collection et utilise la méthode UrlEncode. Le résultat souhaité est déjà renvoyé.

Avec le résultat en main, vous pouvez ensuite l'ajouter à l'URL et rediriger:

var nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
string url = Request.Url.AbsolutePath + "?" + nameValues.ToString();
Response.Redirect(url);

Actuellement, la seule façon d'utiliser HttpValueCollection consiste à utiliser la méthode ParseQueryString présentée ci-dessus (autre que la réflexion, bien sûr). Il semble que cela ne changera pas, car le problème de Connect demandant à ce que cette classe soit rendue publique a été fermé avec le statut "ne sera pas résolu". 

En passant, vous pouvez appeler les méthodes Add, Set et Remove sur nameValues pour modifier l'un des éléments de la chaîne de requête avant de l'ajouter. Si cela vous intéresse voir ma réponse à une autre question .

97
Ahmad Mageed
string q = String.Join("&",
             nvc.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(nvc[a])));
61
Denis

Cela devrait fonctionner sans trop de code:

NameValueCollection nameValues = HttpUtility.ParseQueryString(String.Empty);
nameValues.Add(Request.QueryString);
// modify nameValues if desired
var newUrl = "/page?" + nameValues;

L'idée est d'utiliser HttpUtility.ParseQueryString pour générer une collection vide de type HttpValueCollection. Cette classe est une sous-classe de NameValueCollection qui est marquée par internal afin que votre code ne puisse pas en créer facilement une instance.

La bonne chose à propos de HttpValueCollection est que la méthode ToString s'occupe de l'encodage pour vous. En exploitant la méthode NameValueCollection.Add(NameValueCollection), vous pouvez ajouter les paramètres de chaîne de requête existants à votre objet nouvellement créé sans avoir à convertir au préalable la collection Request.QueryString en une chaîne à codage url, puis à la réanalyser en une collection.

Cette technique peut également être exposée comme méthode d'extension:

public static string ToQueryString(this NameValueCollection nameValueCollection)
{
    NameValueCollection httpValueCollection = HttpUtility.ParseQueryString(String.Empty)
    httpValueCollection.Add(nameValueCollection);
    return httpValueCollection.ToString();
}
11
dana

Créez une méthode d’extension utilisant deux boucles. Je préfère cette solution car elle est lisible (sans linq), ne nécessite pas System.Web.HttpUtility et gère les clés en double.

public static string ToQueryString(this NameValueCollection nvc)
{
    if (nvc == null) return string.Empty;

    StringBuilder sb = new StringBuilder();

    foreach (string key in nvc.Keys)
    {
        if (string.IsNullOrWhiteSpace(key)) continue;

        string[] values = nvc.GetValues(key);
        if (values == null) continue;

        foreach (string value in values)
        {
            sb.Append(sb.Length == 0 ? "?" : "&");
            sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
        }
    }

    return sb.ToString();
}

Exemple

var queryParams = new NameValueCollection()
{
    { "order_id", "0000" },
    { "item_id", "1111" },
    { "item_id", "2222" },
    { null, "skip entry with null key" },
    { "needs escaping", "special chars ? = &" },
    { "skip entry with null value", null }
};

Console.WriteLine(queryParams.ToQueryString());

Sortie

?order_id=0000&item_id=1111&item_id=2222&needs%20escaping=special%20chars%20%3F%20%3D%20%26
11
Mathew Leger

En fait, vous devez également encoder la clé, pas seulement la valeur.

string q = String.Join("&",
nvc.AllKeys.Select(a => $"{HttpUtility.UrlEncode(a)}={HttpUtility.UrlEncode(nvc[a])}"));
5
user2397863

Etant donné qu'une NameValueCollection peut avoir plusieurs valeurs pour la même clé, si vous êtes préoccupé par le format de la chaîne de requête (puisqu'elle sera renvoyée sous forme de valeurs séparées par des virgules plutôt que de "notation de tableau"), vous pouvez prendre en compte les éléments suivants.

Exemple

var nvc = new NameValueCollection();
nvc.Add("key1", "val1");
nvc.Add("key2", "val2");
nvc.Add("empty", null);
nvc.Add("key2", "val2b");

Transformez-vous en: key1=val1&key2[]=val2&empty&key2[]=val2b plutôt que key1=val1&key2=val2,val2b&empty.

Code

string qs = string.Join("&", 
    // "loop" the keys
    nvc.AllKeys.SelectMany(k => {
        // "loop" the values
        var values = nvc.GetValues(k);
        if(values == null) return new[]{ k };
        return nvc.GetValues(k).Select( (v,i) => 
            // 'gracefully' handle formatting
            // when there's 1 or more values
            string.Format(
                values.Length > 1
                    // pick your array format: k[i]=v or k[]=v, etc
                    ? "{0}[]={1}"
                    : "{0}={1}"
                , k, HttpUtility.UrlEncode(v), i)
        );
    })
);

ou si vous n'aimez pas tellement Linq ...

string qs = nvc.ToQueryString(); // using...

public static class UrlExtensions {
    public static string ToQueryString(this NameValueCollection nvc) {
        return string.Join("&", nvc.GetUrlList());
    }

    public static IEnumerable<string> GetUrlList(this NameValueCollection nvc) {
        foreach(var k in nvc.AllKeys) {
            var values = nvc.GetValues(k);
            if(values == null)  { yield return k; continue; }
            for(int i = 0; i < values.Length; i++) {
                yield return
                // 'gracefully' handle formatting
                // when there's 1 or more values
                string.Format(
                    values.Length > 1
                        // pick your array format: k[i]=v or k[]=v, etc
                        ? "{0}[]={1}"
                        : "{0}={1}"
                    , k, HttpUtility.UrlEncode(values[i]), i);
            }
        }
    }
}

Comme cela a déjà été souligné dans les commentaires, à l'exception de cette réponse la plupart des autres réponses traitent du scénario (Request.QueryString est une HttpValueCollection, "pas" une NameValueCollection) plutôt que la question littérale.

Mise à jour: a résolu le problème de valeur null à partir d'un commentaire.

3
drzaus

La réponse courte est d'utiliser .ToString() sur la NameValueCollection et de la combiner avec l'URL d'origine.

Cependant, j'aimerais souligner quelques points:

Vous ne pouvez pas utiliser HttpUtility.ParseQueryString sur Request.RawUrl. La méthode ParseQueryString() recherche une valeur comme celle-ci: ?var=value&var2=value2.

Si vous voulez obtenir une NameValueCollection des paramètres QueryString, utilisez simplement Request.QueryString().

var nv = Request.QueryString;

Pour reconstruire l'URL, utilisez simplement nv.ToString ().

string url = String.Format("{0}?{1}", Request.Path, nv.ToString());

Si vous essayez d'analyser une chaîne d'URL au lieu d'utiliser l'objet Request, utilisez Uri et la méthode HttpUtility.ParseQueryString.

Uri uri = new Uri("<THE URL>");
var nv = HttpUtility.ParseQueryString(uri.Query);
string url = String.Format("{0}?{1}", uri.AbsolutePath, nv.ToString());
3
Jeremy

Dans AspNet Core 2.0, vous pouvez utiliser la méthode QueryHelpers AddQueryString.

1
Atchitutchuk

Est-ce que cela fonctionnerait pour vous?

public static string BuildUrl(string relativeUrl, params string[] queryString)
{
    // build queryString from string parameters
    char[] trimChars = { ' ', '&', '?' };
    StringBuilder builder = new StringBuilder();
    string sepChar = "&";
    string sep = String.Empty;
    foreach (string q in queryString)
    {
        builder.Append(sep).Append(q.Trim(trimChars));
        sep = sepChar;
    }

    if (String.IsNullOrEmpty(builder.ToString())) { return relativeUrl; }
    else { return relativeUrl + "?" + builder.ToString(); }
}

Utilisation:

string url = BuildUrl("/mypage.apsx", "qs1=a", "qs2=b", "qs3=c");
1
Brad

Comme @Atchitutchuk l'a suggéré, vous pouvez utiliser QueryHelpers.AddQueryString dans ASP.NET Core:

    public string FormatParameters(NameValueCollection parameters)
    {
        var queryString = "";
        foreach (var key in parameters.AllKeys)
        {
            foreach (var value in parameters.GetValues(key))
            {
                queryString = QueryHelpers.AddQueryString(queryString, key, value);
            }
        };

        return queryString.TrimStart('?');
    }
0
arni

J'utilise toujours UriBuilder pour convertir une URL avec une chaîne de requête en une URL valide et correctement codée.

var url = "http://my-link.com?foo=bar";

var uriBuilder = new UriBuilder(url);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query.Add("yep", "foo&bar");

uriBuilder.Query = query.ToString();
var result = uriBuilder.ToString();

// http://my-link.com:80/?foo=bar&yep=foo%26bar
0
ms007

Vous pouvez utiliser. 

var ur = new Uri("/page",UriKind.Relative);

si ce nv est de type chaîne, vous pouvez ajouter au premier paramètre uri . 

var ur2 = new Uri("/page?"+nv.ToString(),UriKind.Relative);
0