C # 'da bir URL için sorgu dizesi nasıl oluşturulur?


473

Web kaynaklarını bir koddan çağırırken sık karşılaşılan bir görev, gerekli tüm parametreleri içerecek bir sorgu dizesi oluşturmaktır. Elbette roket bilimi olmasa da, beğenmeniz gereken bazı ayrıntılar var.& olmasa da , ilk parametreyi olmasa bile , parametreleri kodlamak vb.

Bunu yapmak için kod çok basit, ama biraz sıkıcı:

StringBuilder SB = new StringBuilder();
if (NeedsToAddParameter A) 
{ 
  SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA")); 
}

if (NeedsToAddParameter B) 
{
  if (SB.Length>0) SB.Append("&"); 
  SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); }
}

Bu, daha kullanışlı ve okunabilir hale getiren bir yardımcı sınıfın var olmasını bekleyeceği yaygın bir görevdir. MSDN taranırken, bir tane bulamadım - bu da beni şu soruya getiriyor:

Yukarıdakileri yapmanın en zarif ve temiz yolu nedir?


26
Şu anki noktada bile, sorgu dizeleriyle başa çıkmanın basit bir yolu olmadığı biraz üzücü . Ve açıkçası, içsel olmayan, standartlara uygun bir çerçeve sınıfı olan OOB'yi kastediyorum. Ya da belki bir şeyleri kaçırıyorum?
Umutsuzluğun Yüz burcu

5
Hiçbir şey kaçırmıyorsunuz. Querystring binası, Flurl ile doldurmaya çalıştığım çerçevede önemli bir boşluk .
Todd Menier


Sadece bir tane yapmam gerektiğini düşünüyorsun .. yeni UrlBuilder (mevcut) .AddQuery ("anahtar", "değer"). ToString ()
Demetris Leptos

Yanıtlar:


293

Başlık altında bakarsanız QueryString özelliği bir NameValueCollection olur. Benzer şeyleri yaptığımda genellikle serileştirme ve serileştirme ile ilgileniyorum, bu yüzden önerim bir NameValueCollection oluşturmak ve daha sonra geçmek:

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

private string ToQueryString(NameValueCollection nvc)
{
    var array = (
        from key in nvc.AllKeys
        from value in nvc.GetValues(key)
            select string.Format(
                "{0}={1}",
                HttpUtility.UrlEncode(key),
                HttpUtility.UrlEncode(value))
        ).ToArray();
    return "?" + string.Join("&", array);
}

Bunu LINQ'da yapmanın süper zarif bir yolu olduğunu hayal ediyorum ...


22
HTTP spec (RFC 2616), sorgu dizelerinin içerebileceği hakkında hiçbir şey söylemez. Genel URI formatını tanımlayan RFC 3986 da tanımlanmamıştır. Yaygın olarak kullanılan anahtar / değer çifti biçimi, application/x-www-form-urlencodedform verilerini bir GETisteğin parçası olarak göndermek amacıyla çağrılır ve aslında HTML tarafından tanımlanır . HTML 5, bu biçimdeki anahtar başına birden çok değeri yasaklamaz ve aslında sayfanın (yanlış) aynı nameözniteliğe sahip birden çok alan içermesi durumunda tarayıcının anahtar başına birden çok değer üretmesini gerektirir . Bkz goo.gl/uk1Ag
Daniel Cassidy

14
@annakata: Yorumumun bir yıldan daha eski olduğunu biliyorum (ve iki yaşın üzerindeki cevap!), ancak NameValueCollection GetValues ​​(anahtar) yöntemini kullanarak anahtar başına birden çok değeri destekliyor.
Mauricio Scheffer

4
@MauricioScheffer: Ancak NameValueCollection "doğru" bir sorgu dizesine dönüşmez. Örneğin, aynı anahtarın birden çok kez bulunduğu WebClient üzerinde QueryString parametresini ayarlarsanız, ortak bir (standart olan "path? Key = value1 & key = value2" yerine "path? Key = value1, value2" olur. ?) Desen.
David Pope

8
Anahtar başına birden çok değerle ilgili olarak, HTML'de, seçili ve gönderilmiş birden çok öğe içeren bir çoklu seçim liste kutunuz varsa, bunların David tarafından belirtilen çoklu değer biçiminde gönderildiğine inanıyorum.
Sam

10
Daha taşınabilir olan HttpUtility.UrlEncode yerine Uri.EscapeDataString öğesini kullanmak isteyebilirsiniz. Bkz. Stackoverflow.com/questions/2573290/…
PEK

687

Sen yeni bir yazılabilir örneğini oluşturabilir HttpValueCollectionarayarak System.Web.HttpUtility.ParseQueryString(string.Empty)ve ardından herhangi olarak kullanabilirsiniz NameValueCollection. İstediğiniz değerleri ekledikten sonra ToString, aşağıdaki gibi bir sorgu dizesi almak için koleksiyonu arayabilirsiniz :

NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);

queryString.Add("key1", "value1");
queryString.Add("key2", "value2");

return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded

Bu HttpValueCollectioniçseldir ve bu nedenle doğrudan bir örnek oluşturamazsınız. Ancak, bir örneği edindikten sonra bunu diğer tüm örnekler gibi kullanabilirsiniz NameValueCollection. HttpValueCollectionÜzerinde çalıştığınız gerçek nesne bir olduğundan , ToString yöntemini çağırmak HttpValueCollection, koleksiyonu URL kodlu sorgu dizesi olarak biçimlendiren geçersiz kılınmış yöntemi çağırır .

SO ve web'de benzer bir sorunun cevabını aradıktan sonra, bulabildiğim en basit çözüm.

.NET Core

.NET Core'da çalışıyorsanız, bunu Microsoft.AspNetCore.WebUtilities.QueryHelpersbüyük ölçüde basitleştiren sınıfı kullanabilirsiniz .

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers

Basit kod:

const string url = "https://customer-information.azure-api.net/customers/search/taxnbr";
var param = new Dictionary<string, string>() { { "CIKey", "123456789" } };

var newUrl = new Uri(QueryHelpers.AddQueryString(url, param));

6
Muhtemelen IDictionary arayüzü için ToURLQueryString adlı bir uzatma yöntemi oluşturabilirsiniz:public static string ToURLQueryString(this IDictionary dict) { ... }
Roy Tinker

65
Bu yöntem, çok baytlı karakterler için standart uyumlu değildir . Bunları% XX% XX yerine% uXXXX olarak kodlar. Sonuçta ortaya çıkan sorgu dizeleri web sunucuları tarafından yanlış yorumlanabilir. Bu, HttpUtility.ParseQueryString () tarafından döndürülen dahili çerçeve sınıfı HttpValueCollection içinde bile belgelenmiştir. Yorum, bu davranışı geriye dönük uyumluluk nedeniyle koruduklarını söylüyor.
alex

25
HttpUtilityPraseQueryString ("") ve yeni NameValueCollection () arasında önemli bir fark olduğunu unutmayın - uygun bir sorgu
dizisi

7
Sorgu dizesinde adın birden çok örneğini istediğiniz durumlar ne olacak? Örneğin, "type = 10 & type = 21".
Finster 21

7
@Finster AddYöntemi kullanarak sorgu dizesine bir adın birden çok örneğini ekleyebilirsiniz . Yani yöntemi queryString.Add("type", "1"); queryString.Add("type", "2"); kullanmak Addmuhtemelen bunu her zaman yapmanın daha iyi bir yoludur.
jeremysawesome

94

Roy Tinker'in yorumundan ilham alarak, Uri sınıfında kodumu kısa ve temiz tutan basit bir uzatma yöntemi kullandım:

using System.Web;

public static class HttpExtensions
{
    public static Uri AddQuery(this Uri uri, string name, string value)
    {
        var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);

        httpValueCollection.Remove(name);
        httpValueCollection.Add(name, value);

        var ub = new UriBuilder(uri);
        ub.Query = httpValueCollection.ToString();

        return ub.Uri;
    }
}

Kullanımı:

Uri url = new Uri("http://localhost/rest/something/browse").
          AddQuery("page", "0").
          AddQuery("pageSize", "200");

Düzenle - Standartlarla uyumlu varyant

Birkaç kişinin işaret ettiği gibi, httpValueCollection.ToString()Unicode karakterlerini standartlara uygun olmayan bir şekilde kodlar . Bu, HttpUtility.UrlEncodekullanımdan kaldırılmış HttpUtility.UrlEncodeUnicodeyöntem yerine yöntemi çağırarak bu karakterleri işleyen aynı uzantı yönteminin bir varyantıdır .

using System.Web;

public static Uri AddQuery(this Uri uri, string name, string value)
{
    var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);

    httpValueCollection.Remove(name);
    httpValueCollection.Add(name, value);

    var ub = new UriBuilder(uri);

    // this code block is taken from httpValueCollection.ToString() method
    // and modified so it encodes strings with HttpUtility.UrlEncode
    if (httpValueCollection.Count == 0)
        ub.Query = String.Empty;
    else
    {
        var sb = new StringBuilder();

        for (int i = 0; i < httpValueCollection.Count; i++)
        {
            string text = httpValueCollection.GetKey(i);
            {
                text = HttpUtility.UrlEncode(text);

                string val = (text != null) ? (text + "=") : string.Empty;
                string[] vals = httpValueCollection.GetValues(i);

                if (sb.Length > 0)
                    sb.Append('&');

                if (vals == null || vals.Length == 0)
                    sb.Append(val);
                else
                {
                    if (vals.Length == 1)
                    {
                        sb.Append(val);
                        sb.Append(HttpUtility.UrlEncode(vals[0]));
                    }
                    else
                    {
                        for (int j = 0; j < vals.Length; j++)
                        {
                            if (j > 0)
                                sb.Append('&');

                            sb.Append(val);
                            sb.Append(HttpUtility.UrlEncode(vals[j]));
                        }
                    }
                }
            }
        }

        ub.Query = sb.ToString();
    }

    return ub.Uri;
}

3
Mükemmel. Kurum içi kütüphaneme eklendi. :)
Andy

1
URL değerini de kodlamanız gerekir. queryString.Add (ad; Uri.EscapeDataString (değer));
Ufuk Hacıoğulları

2
Bu yanıtı geliştirdiğiniz için teşekkür ederiz.
Ufuk Hacıoğulları

9
Yan not, bu göreli URL'lerle çalışmaz çünkü UriBuilder'ı göreli bir Uri'den başlatamazsınız.
Yuriy Faktorovich

1
Bir yinelenen anahtarın eklenememesi için bir kaldırma anahtarı ekledim. dotnetfiddle.net/hTlyAd
Paul Totzke

29

Bir süre önce benzer bir soruyu cevapladım . Temel olarak, sınıfı kullanmanın en iyi yoluHttpValueCollection ASP.NET'inRequest.QueryString özelliği aslında ne yazık ki, .NET çerçevesinde iç . Reflektörü kullanarak onu yakalayabilirsin (ve Utils sınıfına yerleştirebilirsin). Bu şekilde, sorgu dizesini bir NameValueCollection gibi, ancak tüm url kodlama / kod çözme sorunlarıyla ilgilenerek değiştirebilirsiniz.

HttpValueCollectionuzanır NameValueCollectionve bir alan yapıcıya sahip kodlanmış sorgu dizesi (Ve işareti ve soru işaretleri dahil) ve bir geçersiz kılarToString() sonra altta yatan koleksiyonundan sorgu dizesi yeniden inşa etmek için bir yöntem.

Misal:

  var coll = new HttpValueCollection();

  coll["userId"] = "50";
  coll["paramA"] = "A";
  coll["paramB"] = "B";      

  string query = coll.ToString(true); // true means use urlencode

  Console.WriteLine(query); // prints: userId=50&paramA=A&paramB=B

Teşekkür ederim ... i döndü NameValueCollection farklı davranan ama nedenini anlayamadım bir ToString () olduğunu fark ettim.
calebt

httpValueCollection.ToString()aslında çağırır, httpValueCollection.ToString(true)bu yüzden açıklık eklemek truegerekmez.
dav_i

5
HttpValueCollection dahili bir sınıftır, bu nedenle onu başlatamazsınız.
ozba

29

Burada, aynı anahtar için birden çok değeri destekleyen bir uzantı yöntemi olarak (önceki yazılardaki kavramları birleştirerek) akıcı / lambda-ish yolu verilmiştir. Kişisel tercihim, diğer ekip üyelerinin bu tür şeyler için keşfetme kabiliyetleri için sarmalayıcılar üzerine uzantılar. Kodlayan yöntemleri yığın taşması (bir tür bu konuda mesajların bol etrafında tartışma var olduğunu Not sonrası ) ve (gibi MSDN blogcular bu bir ).

public static string ToQueryString(this NameValueCollection source)
{
    return String.Join("&", source.AllKeys
        .SelectMany(key => source.GetValues(key)
            .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))))
        .ToArray());
}

edit: null desteği ile, muhtemelen kendi durumunuza uyarlamanız gerekir

public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries)
{
    return source != null ? String.Join("&", source.AllKeys
        .Where(key => !removeEmptyEntries || source.GetValues(key)
            .Where(value => !String.IsNullOrEmpty(value))
            .Any())
        .SelectMany(key => source.GetValues(key)
            .Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value))
            .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty)))
        .ToArray())
        : string.Empty;
}

1
Değerlerden herhangi biri boşsa bu başarısız olur
Josh Noe

bu yanlış her anahtar değer çifti için birçok sorgu dizeleri oluşturmak
Gayan

@GayanRanasinghe: Bu ne anlama geliyor?
Matti Virkkunen

22

Flurl [açıklama: Ben yazarım] anonim nesneler (diğer yolların yanı sıra) üzerinden sorgu dizeleri oluşturmayı destekler:

var url = "http://www.some-api.com".SetQueryParams(new
{
    api_key = ConfigurationManager.AppSettings["SomeApiKey"],
    max_results = 20,
    q = "Don't worry, I'll get encoded!"
});

İsteğe bağlı Flurl.Http tamamlayıcı lib'i, aynı akıcı çağrı zincirinden HTTP çağrıları yapmanızı ve tam gelişmiş bir REST istemcisine genişletmenizi sağlar:

T result = await "https://api.mysite.com"
    .AppendPathSegment("person")
    .SetQueryParams(new { ap_key = "my-key" })
    .WithOAuthBearerToken("MyToken")
    .PostJsonAsync(new { first_name = firstName, last_name = lastName })
    .ReceiveJson<T>();

Tam paket NuGet'te mevcuttur:

PM> Install-Package Flurl.Http

veya yalnızca bağımsız URL oluşturucu:

PM> Install-Package Flurl


20

İşte geç girişim. Çeşitli nedenlerle diğerlerinden hoşlanmadım, bu yüzden kendim yazdım.

Bu sürüm özellikleri:

  • Yalnızca StringBuilder kullanımı. ToArray () çağrısı veya diğer uzantı yöntemleri yok. Bazı diğer yanıtlar kadar hoş görünmüyor, ancak bunun temel bir işlev olduğunu düşünüyorum, bu nedenle verimlilik, "akıcı", "tek satırlı" kodlara sahip olmaktan daha fazla önemsizdir.

  • Anahtar başına birden çok değeri işler. (Kendime ihtiyacım yoktu ama Mauricio'yu susturmak için;)

    public string ToQueryString(NameValueCollection nvc)
    {
        StringBuilder sb = new StringBuilder("?");
    
        bool first = true;
    
        foreach (string key in nvc.AllKeys)
        {
            foreach (string value in nvc.GetValues(key))
            {
                if (!first)
                {
                    sb.Append("&");
                }
    
                sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
    
                first = false;
            }
        }
    
        return sb.ToString();
    }

Örnek Kullanım

        var queryParams = new NameValueCollection()
        {
            { "x", "1" },
            { "y", "2" },
            { "foo", "bar" },
            { "foo", "baz" },
            { "special chars", "? = &" },
        };

        string url = "http://example.com/stuff" + ToQueryString(queryParams);

        Console.WriteLine(url);

Çıktı

http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26

Bunun System.Web altında olan ve her yerde bulunmayan HttpUtility kullanmaması hoşuma gidiyor.
Kugel

Linq ve HttpUtility kullanmadığınız için +1. Boş bir sb oluşturmak ve "bool first" değişkeni hendek ve sonra döngüde sb.AppendFormat () önce sb.Append (sb.Length == 0? "?": "&") Var. Şimdi nvc boşsa, yöntem yalnız "?" Yerine boş bir dize döndürür.
Mathew Leger

Bu cevap, birden çok değeri olan tek parametreleri işler. örneğin? id = 1 & id = 3 & id = 2 & id = 9
Mathemats

12

Parametreleri böyle akıcı bir tarzda eklemenize izin veren uzatma yöntemleri oluşturmaya ne dersiniz?

string a = "http://www.somedomain.com/somepage.html"
    .AddQueryParam("A", "TheValueOfA")
    .AddQueryParam("B", "TheValueOfB")
    .AddQueryParam("Z", "TheValueOfZ");

string b = new StringBuilder("http://www.somedomain.com/anotherpage.html")
    .AddQueryParam("A", "TheValueOfA")
    .AddQueryParam("B", "TheValueOfB")
    .AddQueryParam("Z", "TheValueOfZ")
    .ToString(); 

İşte aşırı yükleme string:

public static string AddQueryParam(
    this string source, string key, string value)
{
    string delim;
    if ((source == null) || !source.Contains("?"))
    {
        delim = "?";
    }
    else if (source.EndsWith("?") || source.EndsWith("&"))
    {
        delim = string.Empty;
    }
    else
    {
        delim = "&";
    }

    return source + delim + HttpUtility.UrlEncode(key)
        + "=" + HttpUtility.UrlEncode(value);
}

Ve işte aşağıdakileri kullanan aşırı yük StringBuilder:

public static StringBuilder AddQueryParam(
    this StringBuilder source, string key, string value)
{
    bool hasQuery = false;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == '?')
        {
            hasQuery = true;
            break;
        }
    }

    string delim;
    if (!hasQuery)
    {
        delim = "?";
    }
    else if ((source[source.Length - 1] == '?')
        || (source[source.Length - 1] == '&'))
    {
        delim = string.Empty;
    }
    else
    {
        delim = "&";
    }

    return source.Append(delim).Append(HttpUtility.UrlEncode(key))
        .Append("=").Append(HttpUtility.UrlEncode(value));
}

: +1: Basit String tabanlı uzantı yöntemi için. Diğer cevapların bazıları daha kenar örneğini kapsar, ancak bu benim durum için yeterlidir, ve beni bir inşa etmek gerektirmez NameValueCollection, HttpValueCollectionya da bir Uriilk. Teşekkürler!
Stanley G.

11

Üzerinde çalıştığım bir taşınabilir sınıf kitaplığı (PCL) için aynı sorunu çözmek gerekiyordu. Bu durumda, ParseQueryString'i kullanamadığım için System.Web'e erişimim yok.

Bunun yerine şöyle kullandım System.Net.Http.FormUrlEncodedContent:

var url = new UriBuilder("http://example.com");

url.Query = new FormUrlEncodedContent(new Dictionary<string,string>()
{
    {"param1", "val1"},
    {"param2", "val2"},
    {"param3", "val3"},
}).ReadAsStringAsync().Result;

Bu benim kullandığım tekniktir ve başka bir soruda referans verdim http://stackoverflow.com/a/26744471/2108310 Tek fark, System referansına ihtiyaç duymak dışında bir dizi KeyValue çifti kullanmam. Net (sizin belirttiğiniz gibi PCL mevcuttur) bu, IMHO'yu bazı üçüncü taraf paketlerini içermeden veya bazı homebrew spagetti karışıklığını birlikte çalmaya çalışmanın en basit yoludur.
Rostov

9
    public static string ToQueryString(this Dictionary<string, string> source)
    {
        return String.Join("&", source.Select(kvp => String.Format("{0}={1}", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))).ToArray());
    }

    public static string ToQueryString(this NameValueCollection source)
    {
        return String.Join("&", source.Cast<string>().Select(key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(source[key]))).ToArray());
    }

1
Güzel! Ama .ToArray()s'ye ihtiyacınız yok .
mpen

7

Bu sınıfı projenize ekleyin

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public class QueryStringBuilder
{
    private readonly List<KeyValuePair<string, object>> _list;

    public QueryStringBuilder()
    {
        _list = new List<KeyValuePair<string, object>>();
    }

    public void Add(string name, object value)
    {
        _list.Add(new KeyValuePair<string, object>(name, value));
    }

    public override string ToString()
    {
        return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key), "=", Uri.EscapeDataString(kvp.Value.ToString()))));
    }
}

Ve şu şekilde kullanın:

var actual = new QueryStringBuilder {
    {"foo", 123},
    {"bar", "val31"},
    {"bar", "val32"}
};

actual.Add("a+b", "c+d");

actual.ToString(); // "foo=123&bar=val31&bar=val32&a%2bb=c%2bd"

Şimdi, bu kabul edilen cevap olmalı; "foo [] = 1, foo [] = 2" gibi diziler için mükemmel çalışır ve bu arada çok önemli olan parametrelerin sırasını korur.
Soroush Falahati

6

Teklifim:

public static Uri AddQuery(this Uri uri, string name, string value)
{
    // this actually returns HttpValueCollection : NameValueCollection
    // which uses unicode compliant encoding on ToString()
    var query = HttpUtility.ParseQueryString(uri.Query);

    query.Add(name, value);

    var uriBuilder = new UriBuilder(uri)
    {
        Query = query.ToString()
    };

    return uriBuilder.Uri;
}

Kullanımı:

var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method")
                                             .AddQuery("wow", "soFluent");

// http://stackoverflow.com?such=method&wow=soFluent

Yaklaşımınızı tercih ederim Basit ve Zarif, Ancak, HttpUtility System.Web gerektirir
Ody

5

Test edilmedi, ancak bence bu çizgilerdeki bir şey oldukça iyi çalışır

public class QueryString
{
    private Dictionary<string,string> _Params = new Dictionary<string,string>();

    public overide ToString()
    {
        List<string> returnParams = new List<string>();

        foreach (KeyValuePair param in _Params)
        {
            returnParams.Add(String.Format("{0}={1}", param.Key, param.Value));
        }

        // return String.Format("?{0}", String.Join("&", returnParams.ToArray())); 

        // credit annakata
        return "?" + String.Join("&", returnParams.ToArray());
    }

    public void Add(string key, string value)
    {
        _Params.Add(key, HttpUtility.UrlEncode(value));
    }
}

QueryString query = new QueryString();

query.Add("param1", "value1");
query.Add("param2", "value2");

return query.ToString();

güzel kapsüllenmiş ama "? {0}" üzerindeki biçim gereksiz yere pahalı :)
annakata

sınıf adını QueryString olarak değiştirdi. Sorgu biraz belirsiz
Nick Allen

4

Hızlı uzantı yöntemi tabanlı bir sürüm:

class Program
{
    static void Main(string[] args)
    {
        var parameters = new List<KeyValuePair<string, string>>
                             {
                                 new KeyValuePair<string, string>("A", "AValue"),
                                 new KeyValuePair<string, string>("B", "BValue")
                             };

        string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray());
    }
}

public static class KeyValueExtensions
{
    public static string ToQueryString(this KeyValuePair<string, string> obj)
    {
        return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value);
    }
}

Dizeye hangi parametrelerin ekleneceğini seçmek için where yan tümcesini kullanabilirsiniz.


3

Diğer montajlara olan bağımlılıkları azaltmak ve işleri basit tutmak istediğinizi varsayarsak, şunları yapabilirsiniz:

var sb = new System.Text.StringBuilder();

sb.Append("a=" + HttpUtility.UrlEncode("TheValueOfA") + "&");
sb.Append("b=" + HttpUtility.UrlEncode("TheValueOfB") + "&");
sb.Append("c=" + HttpUtility.UrlEncode("TheValueOfC") + "&");
sb.Append("d=" + HttpUtility.UrlEncode("TheValueOfD") + "&");

sb.Remove(sb.Length-1, 1); // Remove the final '&'

string result = sb.ToString();

Bu, döngülerle de iyi çalışır. Son ve işareti kaldırma işleminin döngü dışına çıkması gerekir.

Birleştirme operatörünün okunabilirliği artırmak için kullanıldığını unutmayın. Bir StringBuilder kullanma maliyeti ile karşılaştırıldığında kullanmanın maliyeti minimumdur (Bence Jeff Atwood bu konuda bir şey gönderdi).


3

Anonim bir nesne sürümü oluşturmak için en iyi yanıtları birleştirdik :

var queryString = HttpUtility2.BuildQueryString(new
{
    key2 = "value2",
    key1 = "value1",
});

Bu şunu üretir:

anahtar2 = deger2 & anahtar1 = deger1

İşte kod:

public static class HttpUtility2
{
    public static string BuildQueryString<T>(T obj)
    {
        var queryString = HttpUtility.ParseQueryString(string.Empty);

        foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>())
        {
            var value = (property.GetValue(obj) ?? "").ToString();
            queryString.Add(property.Name, value);
        }

        return queryString.ToString();
    }
}

2

Kabul edilen çözümle aynı, ancak "dot" LINQ sözdizimine aktarıldı ...

private string ToQueryString(NameValueCollection nvc)
{
    if (nvc == null) return String.Empty;
    var queryParams = 
          string.Join("&", nvc.AllKeys.Select(key => 
              string.Join("&", nvc.GetValues(key).Select(v => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(v))))));
    return "?" + queryParams;
}

2

Uri için bir uzantısı yöntemi var:

  • Anonim nesneleri kabul eder: uri.WithQuery(new { name = "value" })
  • string/stringÇiftlerin koleksiyonlarını kabul eder (örneğin, Sözlük`2 ).
  • string/objectÇiftlerin koleksiyonlarını kabul eder (örn. RouteValueDictionary ).
  • Kabul NameValueCollection s.
  • Aynı değerleri eşit URI'ler üretecek şekilde sorgu değerlerini anahtarla sıralar.
  • Orijinal sıralarını koruyarak anahtar başına birden çok değeri destekler.

Belgelenen versiyonu burada bulabilirsiniz .

Uzantı:

public static Uri WithQuery(this Uri uri, object values)
{
    if (uri == null)
        throw new ArgumentNullException(nameof(uri));

    if (values != null)
    {
        var query = string.Join(
            "&", from p in ParseQueryValues(values)
                 where !string.IsNullOrWhiteSpace(p.Key)
                 let k = HttpUtility.UrlEncode(p.Key.Trim())
                 let v = HttpUtility.UrlEncode(p.Value)
                 orderby k
                 select string.IsNullOrEmpty(v) ? k : $"{k}={v}");

        if (query.Length != 0 || uri.Query.Length != 0)
            uri = new UriBuilder(uri) { Query = query }.Uri;
    }

    return uri;
}

Sorgu ayrıştırıcısı:

private static IEnumerable<KeyValuePair<string, string>> ParseQueryValues(object values)
{
    // Check if a name/value collection.
    var nvc = values as NameValueCollection;
    if (nvc != null)
        return from key in nvc.AllKeys
               from val in nvc.GetValues(key)
               select new KeyValuePair<string, string>(key, val);

    // Check if a string/string dictionary.
    var ssd = values as IEnumerable<KeyValuePair<string, string>>;
    if (ssd != null)
        return ssd;

    // Check if a string/object dictionary.
    var sod = values as IEnumerable<KeyValuePair<string, object>>;
    if (sod == null)
    {
        // Check if a non-generic dictionary.
        var ngd = values as IDictionary;
        if (ngd != null)
            sod = ngd.Cast<dynamic>().ToDictionary<dynamic, string, object>(
                p => p.Key.ToString(), p => p.Value as object);

        // Convert object properties to dictionary.
        if (sod == null)
            sod = new RouteValueDictionary(values);
    }

    // Normalize and return the values.
    return from pair in sod
           from val in pair.Value as IEnumerable<string>
            ?? new[] { pair.Value?.ToString() }
           select new KeyValuePair<string, string>(pair.Key, val);
}

İşte testler:

var uri = new Uri("https://stackoverflow.com/yo?oldKey=oldValue");

// Test with a string/string dictionary.
var q = uri.WithQuery(new Dictionary<string, string>
{
    ["k1"] = string.Empty,
    ["k2"] = null,
    ["k3"] = "v3"
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1&k2&k3=v3"));

// Test with a string/object dictionary.
q = uri.WithQuery(new Dictionary<string, object>
{
    ["k1"] = "v1",
    ["k2"] = new[] { "v2a", "v2b" },
    ["k3"] = null
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3"));

// Test with a name/value collection.
var nvc = new NameValueCollection()
{
    ["k1"] = string.Empty,
    ["k2"] = "v2a"
};

nvc.Add("k2", "v2b");

q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1&k2=v2a&k2=v2b"));

// Test with any dictionary.
q = uri.WithQuery(new Dictionary<int, HashSet<string>>
{
    [1] = new HashSet<string> { "v1" },
    [2] = new HashSet<string> { "v2a", "v2b" },
    [3] = null
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?1=v1&2=v2a&2=v2b&3"));

// Test with an anonymous object.
q = uri.WithQuery(new
{
    k1 = "v1",
    k2 = new[] { "v2a", "v2b" },
    k3 = new List<string> { "v3" },
    k4 = true,
    k5 = null as Queue<string>
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3=v3&k4=True&k5"));

// Keep existing query using a name/value collection.
nvc = HttpUtility.ParseQueryString(uri.Query);
nvc.Add("newKey", "newValue");

q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?newKey=newValue&oldKey=oldValue"));

// Merge two query objects using the RouteValueDictionary.
var an1 = new { k1 = "v1" };
var an2 = new { k2 = "v2" };

q = uri.WithQuery(
    new RouteValueDictionary(an1).Concat(
        new RouteValueDictionary(an2)));

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1=v1&k2=v2"));

2

HttpValueCollection için zincirlenebilir sarmalayıcı sınıfı:

namespace System.Web.Mvc {
    public class QueryStringBuilder {
        private NameValueCollection collection;
        public QueryStringBuilder() {
            collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
        }
        public QueryStringBuilder Add(string key, string value) {
            collection.Add(key, value);
            return this;
        }
        public QueryStringBuilder Remove(string key) {
            collection.Remove(key);
            return this;
        }
        public string this[string key] {
            get { return collection[key]; }
            set { collection[key] = value; }
        }
        public string ToString() {
            return collection.ToString();
        }
    }
}

Örnek kullanım:

QueryStringBuilder parameters = new QueryStringBuilder()
    .Add("view", ViewBag.PageView)
    .Add("page", ViewBag.PageNumber)
    .Add("size", ViewBag.PageSize);
string queryString = parameters.ToString();

1

PageBase sınıfıma aşağıdaki yöntemi ekledim.

protected void Redirect(string url)
    {
        Response.Redirect(url);
    }
protected void Redirect(string url, NameValueCollection querystrings)
    {
        StringBuilder redirectUrl = new StringBuilder(url);

        if (querystrings != null)
        {
            for (int index = 0; index < querystrings.Count; index++)
            {
                if (index == 0)
                {
                    redirectUrl.Append("?");
                }

                redirectUrl.Append(querystrings.Keys[index]);
                redirectUrl.Append("=");
                redirectUrl.Append(HttpUtility.UrlEncode(querystrings[index]));

                if (index < querystrings.Count - 1)
                {
                    redirectUrl.Append("&");
                }
            }
        }

        this.Redirect(redirectUrl.ToString());
    }

Aramak:

NameValueCollection querystrings = new NameValueCollection();    
querystrings.Add("language", "en");
querystrings.Add("id", "134");
this.Redirect("http://www.mypage.com", querystrings);

1

QueryStrings ile çalışırken çok yararlı bulduğum bazı uzantı yöntemleri yazdım. Genellikle geçerli QueryString ile başlamak ve kullanmadan önce değiştirmek istiyorum. Gibi bir şey,

var res = Request.QueryString.Duplicate()
  .ChangeField("field1", "somevalue")
  .ChangeField("field2", "only if following is true", true)
  .ChangeField("id", id, id>0)
  .WriteLocalPathWithQuery(Request.Url)); //Uses context to write the path

Daha fazlası için ve kaynak: http://www.charlesrcook.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx

Temel, ama stili seviyorum.


1

Sadece benim 2 sent atmak istedim:

public static class HttpClientExt
{
    public static Uri AddQueryParams(this Uri uri, string query)
    {
        var ub = new UriBuilder(uri);
        ub.Query = string.IsNullOrEmpty(uri.Query) ? query : string.Join("&", uri.Query.Substring(1), query);
        return ub.Uri;
    }

    public static Uri AddQueryParams(this Uri uri, IEnumerable<string> query)
    {
        return uri.AddQueryParams(string.Join("&", query));
    } 

    public static Uri AddQueryParams(this Uri uri, string key, string value)
    {
        return uri.AddQueryParams(string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)));
    }

    public static Uri AddQueryParams(this Uri uri, params KeyValuePair<string,string>[] kvps)
    {
        return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
    }

    public static Uri AddQueryParams(this Uri uri, IDictionary<string, string> kvps)
    {
        return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
    }

    public static Uri AddQueryParams(this Uri uri, NameValueCollection nvc)
    {
        return uri.AddQueryParams(nvc.AllKeys.SelectMany(nvc.GetValues, (key, value) => string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))));
    }
}

Dokümanlar bunun uri.Querybir? boş değilse .

İçinde HttpUtility.UrlEncodebulunan notSystem.Web .

Kullanımı:

var uri = new Uri("https://api.del.icio.us/v1/posts/suggest").AddQueryParam("url","http://stackoverflow.com")

1

Zarif olmasa da, kullanmayan daha basit bir sürümü seçtim NameValueCollecitons- sadece etrafına sarılmış bir oluşturucu deseni StringBuilder.

public class UrlBuilder
{
    #region Variables / Properties

    private readonly StringBuilder _builder;

    #endregion Variables / Properties

    #region Constructor

    public UrlBuilder(string urlBase)
    {
        _builder = new StringBuilder(urlBase);
    }

    #endregion Constructor

    #region Methods

    public UrlBuilder AppendParameter(string paramName, string value)
    {
        if (_builder.ToString().Contains("?"))
            _builder.Append("&");
        else
            _builder.Append("?");

        _builder.Append(HttpUtility.UrlEncode(paramName));
        _builder.Append("=");
        _builder.Append(HttpUtility.UrlEncode(value));

        return this;
    }

    public override string ToString()
    {
        return _builder.ToString();
    }

    #endregion Methods
}

Mevcut cevaplara göre HttpUtility.UrlEncodearamaları kullandım . Bu şekilde kullanılır:

string url = new UrlBuilder("http://www.somedomain.com/")
             .AppendParameter("a", "true")
             .AppendParameter("b", "muffin")
             .AppendParameter("c", "muffin button")
             .ToString();
// Result: http://www.somedomain.com?a=true&b=muffin&c=muffin%20button

1
// USAGE
[TestMethod]
public void TestUrlBuilder()
{
    Console.WriteLine(
        new UrlBuilder("http://www.google.com?A=B")
            .AddPath("SomePathName")
            .AddPath("AnotherPathName")
            .SetQuery("SomeQueryKey", "SomeQueryValue")
            .AlterQuery("A", x => x + "C"));
}

Çıktı:

http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue

Kod; hepiniz bir şekilde bana bir şekilde teşekkür edebilirsiniz: D

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

// By Demetris Leptos
namespace TheOperator.Foundation.Web
{
    public class UrlBuilder
    {
        public string Scheme { get; set; }

        public string Host { get; set; }

        public int? Port { get; set; }

        public List<string> Paths { get; set; }

        public SortedDictionary<string, string> QueryPairs { get; set; }

        public UrlBuilder(string url)
        {
            this.Paths = new List<string>();
            this.QueryPairs = new SortedDictionary<string, string>();

            string path = null;
            string query = null;
            Uri relativeUri = null;
            if (!Uri.TryCreate(url, UriKind.Relative, out relativeUri))
            {
                var uriBuilder = new UriBuilder(url);
                this.Scheme = uriBuilder.Scheme;
                this.Host = uriBuilder.Host;
                this.Port = uriBuilder.Port;
                path = uriBuilder.Path;
                query = uriBuilder.Query;
            }
            else
            {
                var queryIndex = url.IndexOf('?');
                if (queryIndex >= 0)
                {
                    path = url.Substring(0, queryIndex);
                    query = url.Substring(queryIndex + 1);
                }
                else
                {
                    path = url;
                }
            }
            this.Paths.AddRange(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));
            if (query != null)
            {
                var queryKeyValuePairs = HttpUtility.ParseQueryString(query);
                foreach (var queryKey in queryKeyValuePairs.AllKeys)
                {
                    this.QueryPairs[queryKey] = queryKeyValuePairs[queryKey];
                }
            }
        }

        public UrlBuilder AddPath(string value)
        {
            this.Paths.Add(value);
            return this;
        }

        public UrlBuilder SetQuery(string key, string value)
        {
            this.QueryPairs[key] = value;
            return this;
        }

        public UrlBuilder RemoveQuery(string key)
        {
            this.QueryPairs.Remove(key);
            return this;
        }

        public UrlBuilder AlterQuery(string key, Func<string, string> alterMethod, bool removeOnNull = false)
        {
            string value;
            this.QueryPairs.TryGetValue(key, out value);
            value = alterMethod(value);
            if (removeOnNull && value == null)
            {
                return this.RemoveQuery(key);
            }
            else
            {
                return this.SetQuery(key, value);
            }
        }

        public override string ToString()
        {
            var path = !string.IsNullOrWhiteSpace(this.Host)
                ? string.Join("/", this.Host, string.Join("/", this.Paths))
                : string.Join("/", this.Paths);
            var query = string.Join("&", this.QueryPairs.Select(x => string.Concat(x.Key, "=", HttpUtility.UrlEncode(x.Value))));
            return string.Concat(
                !string.IsNullOrWhiteSpace(this.Scheme) ? string.Concat(this.Scheme, "://") : null,
                path,
                !string.IsNullOrWhiteSpace(query) ? string.Concat("?", query) : null);
        }
    }
}

1

DSO tarafından önerilen çözümle gittim (2 Ağustos 11: 29'da cevaplandı), çözümü HttpUtility kullanmak zorunda değil. Bununla birlikte, Dotnetpearls'da yayınlanan bir makaleye göre , Sözlük kullanmak NameValueCollection kullanmaktan daha hızlıdır (performans olarak). İşte DSO'nun çözümü NameValueCollection yerine Dictionary'i kullanacak şekilde değiştirildi.

    public static Dictionary<string, string> QueryParametersDictionary()
    {
        var dictionary = new Dictionary<string, string>();
        dictionary.Add("name", "John Doe");
        dictionary.Add("address.city", "Seattle");
        dictionary.Add("address.state_code", "WA");
        dictionary.Add("api_key", "5352345263456345635");

        return dictionary;
    }

    public static string ToQueryString(Dictionary<string, string> nvc)
    {
        StringBuilder sb = new StringBuilder();

        bool first = true;

        foreach (KeyValuePair<string, string> pair in nvc)
        {
                if (!first)
                {
                    sb.Append("&");
                }

                sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value));

                first = false;
        }

        return sb.ToString();
    }

1

Sorgu dizesi bir URL'ye şu şekilde eklenebilir:

  1. ad değeri toplama nesnesi yarat
  2. sorgu dizesi öğelerini ve değerlerini bu nesneye ekleyin
  3. Bu ad değeri toplama nesnesini, aşağıdaki linkte sağlanan URL'ye kodlayın

https://blog.codingnovice.com/blog

public ActionResult Create()
{
    //declaring name value collection object
    NameValueCollection collection = new NameValueCollection();

    //adding new value to the name value collection object
    collection.Add("Id1", "wwe323");
    collection.Add("Id2", "454w");
    collection.Add("Id3", "tyt5656");
    collection.Add("Id4", "343wdsd");

    //generating query string
    string url = GenerateQueryString(collection);

    return View();
}

private string GenerateQueryString(NameValueCollection collection)
{
    var querystring = (
        from key in collection.AllKeys
        from value in collection.GetValues(key)
        select string.Format("{0}={1}",
            HttpUtility.UrlEncode(key),
            HttpUtility.UrlEncode(value))
    ).ToArray();
    return "?" + string.Join("&", querystring);
}

0

Ustura projem için diğer cevaplardan bazı ipuçlarını kullanarak bir yardımcı yazdım.

Geçerli isteğin QueryString nesnesini değiştirmemize izin verilmediğinden ParseQueryString işi gereklidir.

@helper GetQueryStringWithValue(string key, string value) {
    var queryString = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString());
    queryString[key] = value;
    @Html.Raw(queryString.ToString())
}

Ben böyle kullanın:

location.search = '?@Helpers.GetQueryStringWithValue("var-name", "var-value")';

Birden fazla değer almasını istiyorsanız, parametreleri bir Sözlüğe değiştirin ve çiftleri sorgu dizesine ekleyin.


0

Aşağıdaki kod HttpValueCollection,ToString , size bir ad = değer sorgu dizesi sağlayan ILSpy aracılığıyla .

Ne yazık ki HttpValueCollection sadece kullanırsanız geri aldığınız bir iç sınıftır HttpUtility.ParseQueryString(). Ben ona tüm viewtate parçaları kaldırıldı ve varsayılan olarak kodlar:

public static class HttpExtensions
{
    public static string ToQueryString(this NameValueCollection collection)
    {
        // This is based off the NameValueCollection.ToString() implementation
        int count = collection.Count;
        if (count == 0)
            return string.Empty;

        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < count; i++)
        {
            string text = collection.GetKey(i);
            text = HttpUtility.UrlEncodeUnicode(text);
            string value = (text != null) ? (text + "=") : string.Empty;
            string[] values = collection.GetValues(i);
            if (stringBuilder.Length > 0)
            {
                stringBuilder.Append('&');
            }
            if (values == null || values.Length == 0)
            {
                stringBuilder.Append(value);
            }
            else
            {
                if (values.Length == 1)
                {
                    stringBuilder.Append(value);
                    string text2 = values[0];
                    text2 = HttpUtility.UrlEncodeUnicode(text2);
                    stringBuilder.Append(text2);
                }
                else
                {
                    for (int j = 0; j < values.Length; j++)
                    {
                        if (j > 0)
                        {
                            stringBuilder.Append('&');
                        }
                        stringBuilder.Append(value);
                        string text2 = values[j];
                        text2 = HttpUtility.UrlEncodeUnicode(text2);
                        stringBuilder.Append(text2);
                    }
                }
            }
        }

        return stringBuilder.ToString();
    }
}

0

Bu, biraz daha kompakt olmasının dışında, kabul edilen cevapla aynıdır:

private string ToQueryString(NameValueCollection nvc)
{
    return "?" + string.Join("&", nvc.AllKeys.Select(k => string.Format("{0}={1}", 
        HttpUtility.UrlEncode(k), 
        HttpUtility.UrlEncode(nvc[k]))));
}

0

Sadece üst yanıtın VB.NET sürümüne ihtiyaç duyanlar için:

Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
    Dim array As String() = nvc.AllKeys.SelectMany(Function(key As String) nvc.GetValues(key), Function(key As String, value As String) String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key), System.Web.HttpUtility.UrlEncode(value))).ToArray()
    Return "?" + String.Join("&", array)
End Function

Ve LINQ'suz sürüm:

Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
    Dim lsParams As New List(Of String)()

    For Each strKey As String In nvc.AllKeys
        Dim astrValue As String() = nvc.GetValues(strKey)

        For Each strValue As String In astrValue
            lsParams.Add(String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)))
        Next ' Next strValue
    Next ' strKey
    Dim astrParams As String() = lsParams.ToArray()
    lsParams.Clear()
    lsParams = Nothing

    Return "?" + String.Join("&", astrParams)
End Function ' ToQueryString

Ve LINQ'suz C # sürümü:

    public static string ToQueryString(System.Collections.Specialized.NameValueCollection nvc)
    {
        List<string> lsParams = new List<string>();

        foreach (string strKey in nvc.AllKeys)
        {
            string[] astrValue = nvc.GetValues(strKey);

            foreach (string strValue in astrValue)
            {
                lsParams.Add(string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)));
            } // Next strValue

        } // Next strKey

        string[] astrParams =lsParams.ToArray();
        lsParams.Clear();
        lsParams = null;

        return "?" + string.Join("&", astrParams);
    } // End Function ToQueryString
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.