แยกวิเคราะห์และแก้ไขสตริงแบบสอบถามใน. NET Core


113

ฉันได้รับ URI สัมบูรณ์ที่มีสตริงการสืบค้น ฉันต้องการเพิ่มค่าต่อท้ายสตริงการสืบค้นอย่างปลอดภัยและเปลี่ยนพารามิเตอร์ที่มีอยู่

ฉันไม่ต้องการที่จะแก้ไข&foo=barหรือใช้นิพจน์ทั่วไปการหลีกเลี่ยง URI นั้นยุ่งยาก แต่ฉันต้องการใช้กลไกในตัวที่ฉันรู้ว่าจะทำสิ่งนี้ได้อย่างถูกต้องและจัดการกับการหลบหนี

ฉันได้พบ ตันของคำตอบว่าการใช้งานทั้งหมด อย่างไรก็ตามเรื่องนี้เป็นหลัก ASP.NET ไม่มี System.Web อื่น ๆ ประกอบอีกต่อไปจึงไม่มาก HttpUtilityHttpUtility

อะไรคือวิธีที่เหมาะสมในการดำเนินการใน ASP.NET Core ในขณะที่กำหนดเป้าหมายไปที่รันไทม์หลัก


ทางเลือกที่จะMicrosoft.AspNet.WebUtiltiesอาจจะเป็นห้องสมุดMono.HttpUtility
ก่ออิฐ

ฉันเขียนโพสต์เหมือนกันดูได้ที่นี่: neelbhatt40.wordpress.com/2017/07/06/…
Neel

2
อัปเดต 2017: .NET Core 2.0 รวมHttpUtilityและParseQueryStringวิธีการแล้ว
KTCO

คำตอบ:


152

หากคุณใช้ ASP.NET Core 1 หรือ 2 คุณสามารถทำได้Microsoft.AspNetCore.WebUtilities.QueryHelpersในแพ็คเกจMicrosoft.AspNetCore.WebUtilities

หากคุณใช้ ASP.NET Core 3.0 หรือสูงกว่าWebUtilitiesตอนนี้เป็นส่วนหนึ่งของ ASP.NET SDK และไม่ต้องการการอ้างอิงแพ็กเกจ nuget แยกต่างหาก

ในการแยกวิเคราะห์ลงในพจนานุกรม:

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

โปรดทราบว่าไม่เหมือนParseQueryStringใน System.Web ซึ่งจะส่งคืนพจนานุกรมประเภทIDictionary<string, string[]>ใน ASP.NET Core 1.x หรือIDictionary<string, StringValues>ใน ASP.NET Core 2.x หรือสูงกว่าดังนั้นค่าจึงเป็นชุดของสตริง นี่คือวิธีที่พจนานุกรมจัดการกับพารามิเตอร์สตริงการสืบค้นหลายรายการที่มีชื่อเดียวกัน

หากคุณต้องการเพิ่มพารามิเตอร์ลงในสตริงการสืบค้นคุณสามารถใช้วิธีอื่นใน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);

การใช้. net core 2.2 คุณสามารถรับสตริงการสืบค้นโดยใช้

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

คุณจะได้รับชุดของคู่คีย์: ค่า - แบบนี้

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

1
FYI แพ็คเกจ WebUtilities ไม่สามารถทำงานร่วมกับ. net core 1.0 คุณอาจต้องการMicrosoft.AspNetCore.WebUtilitiesแทน
ไจ

6
@Jaime สุดยอดการสังเกต! คุณสามารถแก้ไขคำตอบของฉันด้วยการอัปเดตนั้นเพื่อให้คุณได้รับเครดิตได้หรือไม่
vcsjones

3
ฉบับเสร็จสิ้น ยังคงรักษาเนมสเปซเก่าสำหรับกรณีของ. net เวอร์ชันเดิม
ไจ

1
ดูเหมือนว่าการใช้ QueryHelpers.AddQueryString จะ UrlEscape สตริงโดยอัตโนมัติ - สะดวก
Josh

2
ประเภทผลตอบแทนตอนนี้คือ IDictionary <string, StringValues> แทนที่จะเป็น IDictionary <string, string []>
btlog

35

วิธีที่ง่ายและใช้งานง่ายที่สุดในการใช้ URI แบบสัมบูรณ์และจัดการสตริงการสืบค้นโดยใช้แพ็คเกจ ASP.NET Core เท่านั้นสามารถทำได้ในไม่กี่ขั้นตอนง่ายๆ:

ติดตั้งแพ็คเกจ

PM> ติดตั้งแพ็คเกจ Microsoft.AspNetCore.WebUtilities
PM> ติดตั้งแพ็คเกจ Microsoft.AspNetCore.Http.Extensions

ชั้นเรียนที่สำคัญ

เพียงเพื่อชี้ให้พวกเขาออกจากที่นี่มีสองชั้นสิ่งสำคัญที่เราจะใช้: QueryHelpers , StringValues , QueryBuilder

รหัส

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

เพื่อติดตามการเปลี่ยนแปลงใด ๆ คุณสามารถดูบล็อกโพสต์ของฉันเกี่ยวกับเรื่องนี้ได้ที่นี่: http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/


17

HttpRequestมีQueryคุณสมบัติที่แสดงสตริงแบบสอบถามที่แยกวิเคราะห์ผ่าน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; }

การสนทนาเกี่ยวกับ GitHub นี้ชี้ให้เห็นเช่นกัน


10

ฟังก์ชันนี้จะส่งคืนDictionary<string, string>และไม่ใช้Microsoft.xxxเพื่อความเข้ากันได้

ยอมรับการเข้ารหัสพารามิเตอร์ทั้งสองด้าน

ยอมรับคีย์ที่ซ้ำกัน (คืนค่าสุดท้าย)

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

วิธีนี้ใช้งานได้ แต่คุณควรเพิ่มการตรวจสอบดัชนีก่อนที่จะเริ่มสตริงย่อยเนื่องจากมีความเป็นไปได้แถวนั้นไม่มี '=' ซึ่งทำให้เกิดข้อยกเว้น
Taurib

1
ขอบคุณ @Taurib สำหรับความช่วยเหลือที่เปลี่ยนแปลง
Wagner Pereira

1
คำเตือน: สิ่งนี้จะใช้ไม่ได้หากมีอาร์เรย์ในแบบสอบถามเนื่องจากพจนานุกรมถูกตั้งค่าไว้ <string, string>! (ตัวอย่างเช่น "? item = 1 & item = 2") วิธีแก้ปัญหา: ใช้ IEnumerable <KeyValuePair <string, string >> หรือ Dictionary <string, StringValues> สำหรับ. net core 3.1
theCuriousOne

ขอบคุณ @theCuriousOne ในรูทีนนี้ให้ส่งคืนค่าสุดท้าย "ยอมรับคีย์ที่ซ้ำกัน (คืนค่าสุดท้าย)" เพื่อความเรียบง่ายโซลูชันของคุณสามารถคืนค่าทั้งหมดได้
Wagner Pereira

1

สิ่งสำคัญคือต้องทราบว่าในช่วงเวลาที่คำตอบด้านบนถูกตั้งค่าสถานะว่าถูกต้องซึ่งMicrosoft.AspNetCore.WebUtilitiesมีการอัปเดตเวอร์ชันหลัก (จาก 1.xx ถึง 2.xx)

ที่กล่าวว่าหากคุณกำลังสร้างขึ้นมาnetcoreapp1.1คุณจะต้องเรียกใช้สิ่งต่อไปนี้ซึ่งติดตั้งเวอร์ชันล่าสุดที่รองรับ1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2


1

ฉันใช้วิธีนี้เป็นวิธีการขยายซึ่งใช้ได้กับพารามิเตอร์จำนวนเท่าใดก็ได้:

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

ตัวอย่างลิงก์ถัดไปและก่อนหน้าในมุมมอง:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.