ผนวกค่ากับสตริงการสืบค้น


162

ฉันได้ตั้งค่า URL คล้ายกับรายการด้านล่างในรายการ

ฉันจัดการเพื่อรับสตริงการสืบค้นโดยใช้รหัสต่อไปนี้:

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

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

แต่มันส่งกลับเฉพาะพารามิเตอร์เช่น ID , เซิร์ฟเวอร์ , สถานที่และอื่น ๆ ขึ้นอยู่กับ URL ที่ระบุไว้

สิ่งที่ฉันต้องการคือการเพิ่ม / ผนวกค่ากับสตริงการสืบค้นที่มีอยู่

ตัวอย่างเช่นกับ URL:

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

ฉันต้องการแก้ไขค่าของพารามิเตอร์สตริงการสืบค้น:

การกระทำ = login1

พยายาม = 11

อย่างที่คุณเห็นฉันได้ต่อท้าย "1" สำหรับแต่ละค่า ฉันต้องการรับชุดของ URL จากสตริงที่มีสตริงการสืบค้นที่แตกต่างกันในพวกเขาและเพิ่มค่าให้กับแต่ละพารามิเตอร์ในตอนท้าย & เพิ่มอีกครั้งลงในรายการ

คำตอบ:


329

คุณสามารถใช้HttpUtility.ParseQueryStringวิธีการและวิธีUriBuilderนี้เป็นวิธีที่ดีในการทำงานกับพารามิเตอร์สตริงข้อความค้นหาโดยไม่ต้องกังวลเกี่ยวกับสิ่งต่าง ๆ เช่นการแยกวิเคราะห์การเข้ารหัส 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"

5
อย่างที่คุณเห็นจากตัวอย่างของฉันคุณสามารถใช้ชื่อตัวแปรสำหรับพารามิเตอร์ และนั่นคือสิ่งที่มันทำ: มันผนวกพารามิเตอร์ 2 ตัวเข้ากับ URL ที่มีอยู่ซึ่งฉันได้ฮาร์ดโค้ดไว้ที่นี่ แต่พวกมันสามารถปรับได้อย่างสมบูรณ์แบบไดนามิก
ดารินทร์ดิมิทรอฟ

1
เราไม่ควรใช้HttpUtility.UrlEncode()เมื่อกำหนดค่าพารามิเตอร์?
UserControl

1
@UserControl ไม่ใช่HttpUtility.ParseQueryStringวิธีส่งคืนการปรับใช้ NameValueCollection พิเศษซึ่งจัดการเรื่องนี้ไว้เบื้องหลังเมื่อคุณตั้งค่า
ดารินทินดิมิทรอฟ

5
น่าเสียดายที่สิ่งนี้มีการพึ่งพา System.Web: /
Pure.Krome

4
เป็นที่น่าสังเกตว่าวิธีการนี้สามารถทำให้เกิดปัญหากับความเป็นสากลได้เนื่องจากอักขระพิเศษจะถูกแปลงเป็นค่าเทียบเท่ายูนิโค้ดของพวกเขาในวิธีการแบบสอบถาม ToString ()
tezromania

105

ฉันได้รวบรวมคำตอบของดารินไว้ในวิธีการต่อเติมที่นำมาใช้ซ้ำได้อย่างดี

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

ฉันหวังว่านี่จะช่วยได้!


55

คำตอบที่ให้ไว้มีปัญหากับ URL ของญาติเช่น "/ some / path /" นี่เป็นข้อ จำกัด ของคลาส Uri และ UriBuilder ซึ่งค่อนข้างยากที่จะเข้าใจเนื่องจากฉันไม่เห็นเหตุผลว่าทำไม URL ของญาติจะเป็นปัญหา เมื่อมันมาถึงการจัดการแบบสอบถาม

นี่เป็นวิธีแก้ปัญหาที่ใช้ได้กับทั้งเส้นทางสัมบูรณ์และเส้นทางสัมพัทธ์ซึ่งเขียนและทดสอบใน. NET 4:

(หมายเหตุเล็ก ๆ นี้ยังควรทำงานใน .NET 4.5 คุณเท่านั้นที่จะมีการเปลี่ยนแปลงpropInfo.GetValue(values, null)ไป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; }
      ));
    }
  }

และนี่คือชุดการทดสอบหน่วยเพื่อทดสอบพฤติกรรม:

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

น่าเสียดายที่โซลูชันนี้ใช้ไม่ได้กับ ASP.NET 5 ที่ใช้ cloud .NET เนื่องจาก HttpUtility ดูเหมือนจะไม่สามารถใช้งานได้ แต่มันเป็นทางออกที่ดี ดูstackoverflow.com/questions/29992848/…
diegosasw

1
"Add_to_query_string_dictionary_when_url_contain_hash_and_no_query_string_should_add_values" ควรทดสอบว่า URL กลายเป็นdomain.com/test?param1=val1¶m2=val2#div
gliljas

โปรดตอบโต้การตรวจสอบว่าคุณไม่ได้ใช้งานที่ดีกว่าuri.AbsoluteUriแทนการใช้เอuri.ToString()ฟเฟกต์ที่ไม่น่ารังเกียจ
นิโก้

2
นอกจากนี้: uri.AbsoluteUriพ่นถ้า uri ไม่สมบูรณ์!
Nico

19

หมายเหตุคุณสามารถเพิ่มMicrosoft.AspNetCore.WebUtilitiesแพ็คเกจ nuget จาก Microsoft จากนั้นใช้สิ่งนี้เพื่อผนวกค่ากับสตริงการสืบค้น:

QueryHelpers.AddQueryString(longurl, "action", "login1")
QueryHelpers.AddQueryString(longurl, new Dictionary<string, string> { { "action", "login1" }, { "attempts", "11" } });

3
ในฐานะของ ASP.NET Core 3.0 WebUtilities ขณะนี้เป็นส่วนหนึ่งของ ASP.NET SDK ดังนั้นไม่จำเป็นต้องมีแพ็คเกจแพ็คเกจ
user1069816

1
ปัญหาAddQueryStringคือมันมักจะเพิ่มถ้ามีคีย์อยู่แล้วมันจะไม่อัปเดต แต่สร้างคีย์ที่ซ้ำกันด้วยไม่ดี
Vencovsky

11

โซลูชันต่อไปนี้ใช้งานได้กับ ASP.NET 5 (vNext) และใช้คลาส QueryHelpers เพื่อสร้าง URI พร้อมพารามิเตอร์

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

ผลลัพธ์ URI จะมี http://iberia.com?param1=value1&param2=value2&param3=value3


ปัญหาเดียวของการใช้พจนานุกรมเป็นที่จัดเก็บคีย์แบบสอบถามและค่าคือสตริงการสืบค้นสามารถมีคีย์ซ้ำที่มีค่าต่างกัน ฉันเชื่อว่าคำขอไปยังไซต์ ASP.NET แยกวิเคราะห์ว่าเป็นอาร์เรย์ของค่าสำหรับคีย์นั้น
เซท

2

จุดสิ้นสุดของการแก้ไขสตริงการสืบค้น URL ทั้งหมดจะสิ้นสุดลง

หลังจากทำงานหนักและเล่นซอกับคลาส Uri และวิธีแก้ปัญหาอื่น ๆต่อไปนี้คือวิธีส่วนขยายสตริงของฉันเพื่อแก้ไขปัญหาของฉัน

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

1
ฉันไม่แนะนำให้คนอื่นใช้ raw strings เพื่อเป็นตัวแทนของ URL เช่นนี้เนื่องจากUriชั้นเรียนมีอยู่แล้วเพื่อจุดประสงค์นั้น ใช้อย่างใดอย่างหนึ่งหรือสร้างสิ่งที่เป็นนามธรรมใหม่หากคุณสมบัติหายไป
julealgon

0

ฉันชอบคำตอบของ Bjorn แต่วิธีการแก้ปัญหาที่เขาให้นั้นทำให้เข้าใจผิดเนื่องจากวิธีการอัปเดตพารามิเตอร์ที่มีอยู่แทนที่จะเพิ่มถ้าไม่มีอยู่จริง .. เพื่อให้ปลอดภัยกว่านี้ฉันได้ปรับใช้ด้านล่างนี้

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

ฉันทำการแก้ไขรหัสส่วนเล็กน้อยจริงๆฉันไม่ได้ให้รหัส (OP ทำ) ... ความแตกต่างจะเกิดอะไรขึ้น

1
ถ้า / else ไม่จำเป็นต้องทำquery[paramName] = paramValue;ในทุกกรณี มันมีอยู่มันจะถูกเขียนทับ หากไม่มีอยู่คีย์จะถูกสร้างขึ้น
Richard

-4

วิธีการของฉันง่ายมากสำหรับผู้เริ่มต้น:

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

    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.