Ajouter des valeurs à la chaîne de requête

162

J'ai un ensemble d'URL similaires à celles ci-dessous dans une liste

J'ai réussi à obtenir les chaînes de requête en utilisant le code suivant:

myurl = longurl.Split('?');
NameValueCollection qs = HttpUtility.ParseQueryString(myurl [1]);

foreach (string lol in qs)
{
    // results will return
}

Mais il ne renvoie que les paramètres tels que l' id , le serveur , l' emplacement , etc. en fonction de l'URL fournie.

Ce dont j'ai besoin est d'ajouter / ajouter des valeurs aux chaînes de requête existantes.

Par exemple avec l'URL:

http://somesite.com/backup/index.php?action=login&attempts=1

J'ai besoin de modifier les valeurs des paramètres de la chaîne de requête:

action = login1

tentatives = 11

Comme vous pouvez le voir, j'ai ajouté "1" pour chaque valeur. J'ai besoin d'obtenir un ensemble d'URL à partir d'une chaîne contenant différentes chaînes de requête et d'ajouter une valeur à chaque paramètre à la fin et de les ajouter à nouveau à une liste.

DriverBoy
la source

Réponses:

329

Vous pouvez utiliser la HttpUtility.ParseQueryStringméthode et un UriBuilderqui fournit un moyen agréable de travailler avec les paramètres de chaîne de requête sans vous soucier de choses comme l'analyse, l'encodage d'URL, ...:

string longurl = "http://somesite.com/news.php?article=1&lang=en";
var uriBuilder = new UriBuilder(longurl);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query["action"] = "login1";
query["attempts"] = "11";
uriBuilder.Query = query.ToString();
longurl = uriBuilder.ToString();
// "http://somesite.com:80/news.php?article=1&lang=en&action=login1&attempts=11"
Darin Dimitrov
la source
5
Comme vous pouvez le voir dans mon exemple, vous pouvez utiliser des noms de variables pour les paramètres. Et c'est exactement ce qu'il fait: il ajoute 2 paramètres à l'url existante que j'ai codée en dur ici mais ils pourraient parfaitement être dynamiques.
Darin Dimitrov
1
Ne devrions-nous pas utiliser HttpUtility.UrlEncode()lors de l'attribution d'une valeur de paramètre?
UserControl
1
@UserControl, non, la HttpUtility.ParseQueryStringméthode retourne une implémentation spéciale de NameValueCollection qui gère déjà cela en arrière-plan lorsque vous définissez une valeur.
Darin Dimitrov
5
Dommage que cela ait une dépendance sur System.Web: /
Pure.Krome
4
il convient de noter que cette approche peut entraîner des problèmes d'internationalisation car les caractères spéciaux seront convertis en leurs équivalents unicode dans la méthode query.ToString ().
tezromania
105

J'ai enveloppé la réponse de Darin dans une méthode d'extension joliment réutilisable.

public static class UriExtensions
{
    /// <summary>
    /// Adds the specified parameter to the Query String.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="paramName">Name of the parameter to add.</param>
    /// <param name="paramValue">Value for the parameter to add.</param>
    /// <returns>Url with added parameter.</returns>
    public static Uri AddParameter(this Uri url, string paramName, string paramValue)
    {
        var uriBuilder = new UriBuilder(url);
        var query = HttpUtility.ParseQueryString(uriBuilder.Query);
        query[paramName] = paramValue;
        uriBuilder.Query = query.ToString();

        return uriBuilder.Uri;
    }
}

J'espère que ça aide!

Brinkie
la source
55

Les réponses fournies ont des problèmes avec les URL relatives, telles que "/ some / path /". Ceci est une limitation de la classe Uri et UriBuilder, ce qui est assez difficile à comprendre, car je ne vois aucune raison pour laquelle les URL relatives seraient problématiques lorsqu'il s'agit de manipulation de requêtes.

Voici une solution de contournement qui fonctionne pour les chemins absolus et relatifs, écrits et testés dans .NET 4:

(petite note: cela devrait également fonctionner dans .NET 4.5, il vous suffira de changer propInfo.GetValue(values, null)pour propInfo.GetValue(values))

  public static class UriExtensions{
    /// <summary>
    ///     Adds query string value to an existing url, both absolute and relative URI's are supported.
    /// </summary>
    /// <example>
    /// <code>
    ///     // returns "www.domain.com/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("www.domain.com/test?param1=val1").ExtendQuery(new Dictionary&lt;string, string&gt; { { "param2", "val2" }, { "param3", "val3" } }); 
    /// 
    ///     // returns "/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("/test?param1=val1").ExtendQuery(new Dictionary&lt;string, string&gt; { { "param2", "val2" }, { "param3", "val3" } }); 
    /// </code>
    /// </example>
    /// <param name="uri"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public static Uri ExtendQuery(this Uri uri, IDictionary<string, string> values) {
      var baseUrl = uri.ToString();
      var queryString = string.Empty;
      if (baseUrl.Contains("?")) {
        var urlSplit = baseUrl.Split('?');
        baseUrl = urlSplit[0];
        queryString = urlSplit.Length > 1 ? urlSplit[1] : string.Empty;
      }

      NameValueCollection queryCollection = HttpUtility.ParseQueryString(queryString);
      foreach (var kvp in values ?? new Dictionary<string, string>()) {
        queryCollection[kvp.Key] = kvp.Value;
      }
      var uriKind = uri.IsAbsoluteUri ? UriKind.Absolute : UriKind.Relative;
      return queryCollection.Count == 0 
        ? new Uri(baseUrl, uriKind) 
        : new Uri(string.Format("{0}?{1}", baseUrl, queryCollection), uriKind);
    }

    /// <summary>
    ///     Adds query string value to an existing url, both absolute and relative URI's are supported.
    /// </summary>
    /// <example>
    /// <code>
    ///     // returns "www.domain.com/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("www.domain.com/test?param1=val1").ExtendQuery(new { param2 = "val2", param3 = "val3" }); 
    /// 
    ///     // returns "/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("/test?param1=val1").ExtendQuery(new { param2 = "val2", param3 = "val3" }); 
    /// </code>
    /// </example>
    /// <param name="uri"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public static Uri ExtendQuery(this Uri uri, object values) {
      return ExtendQuery(uri, values.GetType().GetProperties().ToDictionary
      (
          propInfo => propInfo.Name,
          propInfo => { var value = propInfo.GetValue(values, null); return value != null ? value.ToString() : null; }
      ));
    }
  }

Et voici une suite de tests unitaires pour tester le comportement:

  [TestFixture]
  public class UriExtensionsTests {
    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_no_query_string_and_values_is_empty_should_return_url_without_changing_it() {
      Uri url = new Uri("http://www.domain.com/test");
      var values = new Dictionary<string, string>();
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_hash_and_query_string_values_are_empty_should_return_url_without_changing_it() {
      Uri url = new Uri("http://www.domain.com/test#div");
      var values = new Dictionary<string, string>();
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test#div")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_no_query_string_should_add_values() {
      Uri url = new Uri("http://www.domain.com/test");
      var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values() {
      Uri url = new Uri("http://www.domain.com/test#div");
      var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test#div?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("http://www.domain.com/test?param1=val1");
      var values = new Dictionary<string, string> { { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_is_relative_contains_no_query_string_should_add_values() {
      Uri url = new Uri("/test", UriKind.Relative);
      var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_is_relative_and_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new Dictionary<string, string> { { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_is_relative_and_contains_query_string_with_existing_value_should_add_new_values_and_update_existing_ones() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new Dictionary<string, string> { { "param1", "new-value" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=new-value&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_object_when_url_contains_no_query_string_should_add_values() {
      Uri url = new Uri("http://www.domain.com/test");
      var values = new { param1 = "val1", param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }


    [Test]
    public void Add_to_query_string_object_when_url_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("http://www.domain.com/test?param1=val1");
      var values = new { param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_object_when_url_is_relative_contains_no_query_string_should_add_values() {
      Uri url = new Uri("/test", UriKind.Relative);
      var values = new { param1 = "val1", param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_object_when_url_is_relative_and_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new { param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_object_when_url_is_relative_and_contains_query_string_with_existing_value_should_add_new_values_and_update_existing_ones() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new { param1 = "new-value", param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=new-value&param2=val2", UriKind.Relative)));
    }
  }
Moeri
la source
Malheureusement, cette solution ne fonctionne pas pour ASP.NET 5 utilisant le cloud .NET car HttpUtility ne semble pas être disponible. Mais c'est une excellente solution autrement. Voir stackoverflow.com/questions/29992848/…
diegosasw
1
"Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values" devrait tester que l'URL devient domain.com/test?param1=val1¶m2=val2#div
gliljas
Veuillez contre-vérifier si vous ne feriez pas mieux d'utiliser uri.AbsoluteUriplutôt uri.ToString()qu'en raison d'effets désagréables qui ne vous échappent pas.
Nico
2
Ajout: uri.AbsoluteUrijette, si l'URI n'est pas absolu!
Nico
19

Notez que vous pouvez ajouter le Microsoft.AspNetCore.WebUtilitiespackage nuget de Microsoft, puis l'utiliser pour ajouter des valeurs à la chaîne de requête:

QueryHelpers.AddQueryString(longurl, "action", "login1")
QueryHelpers.AddQueryString(longurl, new Dictionary<string, string> { { "action", "login1" }, { "attempts", "11" } });
Michael
la source
3
À partir d'ASP.NET Core 3.0, WebUtilities fait désormais partie du SDK ASP.NET, donc aucun package nuget n'est nécessaire.
user1069816
1
Le problème avec AddQueryStringest qu'il ajoutera toujours, s'il y a déjà la clé, il ne se mettra pas à jour, mais créera des clés en double, avec c'est mauvais
Vencovsky
11

La solution suivante fonctionne pour ASP.NET 5 (vNext) et utilise la classe QueryHelpers pour créer un URI avec des paramètres.

    public Uri GetUri()
    {
        var location = _config.Get("http://iberia.com");
        Dictionary<string, string> values = GetDictionaryParameters();

        var uri = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(location, values);
        return new Uri(uri);
    }

    private Dictionary<string,string> GetDictionaryParameters()
    {
        Dictionary<string, string> values = new Dictionary<string, string>
        {
            { "param1", "value1" },
            { "param2", "value2"},
            { "param3", "value3"}
        };
        return values;
    }

Le résultat URI aurait http://iberia.com?param1=value1&param2=value2&param3=value3

diegosasw
la source
Le seul problème lié à l'utilisation d'un dictionnaire comme magasin de clés de requête et de valeurs est que les chaînes de requête peuvent avoir des clés en double avec des valeurs différentes. Je crois qu'une demande adressée à un site ASP.NET analyse cela comme un tableau de valeurs pour cette clé.
Seth
2

La fin de tous les problèmes d'édition de chaîne de requête URL

Après beaucoup de travail et de bidouillage avec la classe Uri et d'autres solutions, voici mes méthodes d'extension de chaîne pour résoudre mes problèmes.

using System;
using System.Collections.Specialized;
using System.Linq;
using System.Web;

public static class StringExtensions
{
    public static string AddToQueryString(this string url, params object[] keysAndValues)
    {
        return UpdateQueryString(url, q =>
        {
            for (var i = 0; i < keysAndValues.Length; i += 2)
            {
                q.Set(keysAndValues[i].ToString(), keysAndValues[i + 1].ToString());
            }
        });
    }

    public static string RemoveFromQueryString(this string url, params string[] keys)
    {
        return UpdateQueryString(url, q =>
        {
            foreach (var key in keys)
            {
                q.Remove(key);
            }
        });
    }

    public static string UpdateQueryString(string url, Action<NameValueCollection> func)
    {
        var urlWithoutQueryString = url.Contains('?') ? url.Substring(0, url.IndexOf('?')) : url;
        var queryString = url.Contains('?') ? url.Substring(url.IndexOf('?')) : null;
        var query = HttpUtility.ParseQueryString(queryString ?? string.Empty);

        func(query);

        return urlWithoutQueryString + (query.Count > 0 ? "?" : string.Empty) + query;
    }
}

la source
1
Je découragerais les gens d'utiliser des strings bruts pour représenter des URL comme celle-ci, étant donné que la Uriclasse existe déjà à cette fin. Utilisez-le ou créez une toute nouvelle abstraction si des fonctionnalités manquent.
julealgon
0

J'aime la réponse de Bjorn, mais la solution qu'il a fournie est trompeuse, car la méthode met à jour un paramètre existant, plutôt que de l'ajouter s'il n'existe pas. Pour le rendre un peu plus sûr, je l'ai adapté ci-dessous.

public static class UriExtensions
{
    /// <summary>
    /// Adds or Updates the specified parameter to the Query String.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="paramName">Name of the parameter to add.</param>
    /// <param name="paramValue">Value for the parameter to add.</param>
    /// <returns>Url with added parameter.</returns>
    public static Uri AddOrUpdateParameter(this Uri url, string paramName, string paramValue)
    {
        var uriBuilder = new UriBuilder(url);
        var query = HttpUtility.ParseQueryString(uriBuilder.Query);

        if (query.AllKeys.Contains(paramName))
        {
            query[paramName] = paramValue;
        }
        else
        {
            query.Add(paramName, paramValue);
        }
        uriBuilder.Query = query.ToString();

        return uriBuilder.Uri;
    }
}
Stevieboy84
la source
J'ai vraiment fait une modification marginale du code, je ne l'ai pas fourni (l'OP l'a fait) ... quelle sera la différence cependant?
1
Le if / else n'est pas nécessaire, faites simplement query[paramName] = paramValue;dans tous les cas. Il existe, il sera remplacé. S'il n'existe pas, la clé sera créée.
Richard
-4

Mon approche est très simple, pour les débutants:

// --> Prototype : https://ctrader.guru/?id=1#reload

    public static string AddGetParamToUrl(string url, string pname, string pvalue)
    { 

        pvalue = Uri.EscapeDataString(pvalue);

        if (url.IndexOf("?") > -1)
        {

            url = url.Replace("?", string.Format("?{0}={1}&", pname, pvalue));

        }
        else if (url.IndexOf("#") > -1)
        {

            url = url.Replace("#", string.Format("?{0}={1}#", pname, pvalue));

        }
        else
        {

            url = string.Format("{0}?{1}={2}", url, pname, pvalue);

        }

        return url;

    }
Leonardo Ciaccio
la source