Phân tích cú pháp và sửa đổi chuỗi truy vấn trong .NET Core


112

Tôi được cấp một URI tuyệt đối chứa chuỗi truy vấn. Tôi đang tìm cách nối một giá trị vào chuỗi truy vấn một cách an toàn và thay đổi một tham số hiện có.

Tôi không muốn &foo=barsử dụng hoặc sử dụng các biểu thức chính quy, việc thoát URI rất khó. Thay vì tôi muốn sử dụng một cơ chế tích hợp sẵn mà tôi biết sẽ thực hiện điều này một cách chính xác và xử lý việc thoát.

Tôi đã tìm thấy một tấn các câu trả lời rằng tất cả sử dụng HttpUtility. Tuy nhiên đây là ASP.NET Core, không còn lắp ráp System.Web nữa, do đó không còn nữa HttpUtility.

Cách thích hợp để thực hiện việc này trong ASP.NET Core trong khi nhắm mục tiêu thời gian chạy lõi là gì?


Một thay thế Microsoft.AspNet.WebUtiltiescó thể là Mono.HttpUtilitythư viện .
thợ nề

Tôi đã viết một bài cho cùng, có một cái nhìn ở đây: neelbhatt40.wordpress.com/2017/07/06/...
Neel

2
Cập nhật 2017: .NET Core 2.0 hiện bao gồm HttpUtilityParseQueryStringphương pháp.
KTCO

Câu trả lời:


152

Nếu bạn đang sử dụng ASP.NET Core 1 hoặc 2, bạn có thể thực hiện việc này Microsoft.AspNetCore.WebUtilities.QueryHelperstrong gói Microsoft.AspNetCore.WebUtilities .

Nếu bạn đang sử dụng ASP.NET Core 3.0 trở lên, WebUtilitieshiện là một phần của ASP.NET SDK và không yêu cầu tham chiếu gói nuget riêng biệt.

Để phân tích cú pháp thành từ điển:

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

Lưu ý rằng không giống như ParseQueryStringtrong System.Web, điều này trả về một từ điển kiểu IDictionary<string, string[]>trong ASP.NET Core 1.x hoặc IDictionary<string, StringValues>trong ASP.NET Core 2.x trở lên, vì vậy giá trị là một tập hợp các chuỗi. Đây là cách từ điển xử lý nhiều tham số chuỗi truy vấn có cùng tên.

Nếu bạn muốn thêm một tham số vào chuỗi truy vấn, bạn có thể sử dụng một phương pháp khác trên 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);

Sử dụng .net core 2.2, bạn có thể lấy chuỗi truy vấn bằng cách sử dụng

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

Bạn sẽ nhận được một bộ sưu tập các cặp khóa: giá trị - như thế này

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

1
FYI, gói WebUtilities không tương thích với .net core 1.0. Bạn có thể cần Microsoft.AspNetCore.WebUtilitiesthay thế.
Jaime

6
@Jaime Quan sát tuyệt vời! Bạn có thể chỉnh sửa câu trả lời của tôi với bản cập nhật đó để bạn nhận được tín dụng cho nó không?
vcsjones

3
Phiên bản hoàn thành. Cũng giữ không gian tên cũ cho trường hợp của các phiên bản .net kế thừa.
Jaime

1
Có vẻ như việc sử dụng QueryHelpers.AddQueryString sẽ tự động UrlEscape các chuỗi - rất tiện.
Josh

2
Kiểu trả về tại là IDictionary <string, StringValues> chứ không phải IDictionary <string, string []>
btlog

35

Cách dễ nhất và trực quan nhất để lấy một URI tuyệt đối và thao tác với chuỗi truy vấn của nó chỉ bằng gói ASP.NET Core, có thể được thực hiện trong một vài bước đơn giản:

Cài đặt các gói

PM> Install-Package Microsoft.AspNetCore.WebUtilities
PM> Install-Package Microsoft.AspNetCore.Http.Extensions

Các lớp quan trọng

Chỉ cần chỉ ra chúng, đây là hai lớp quan trọng mà chúng ta sẽ sử dụng: QueryHelpers , StringValues , QueryBuilder .

Mật mã

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

Để cập nhật bất kỳ thay đổi nào, bạn có thể xem bài đăng trên blog của tôi về điều này tại đây: http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/


17

HttpRequestcó thuộc Querytính hiển thị chuỗi truy vấn được phân tích cú pháp qua IReadableStringCollectiongiao diện:

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

Cuộc thảo luận này trên GitHub cũng chỉ ra điều đó.


10

Hàm này trả về Dictionary<string, string>và không sử dụng Microsoft.xxxđể tương thích

Chấp nhận mã hóa tham số ở cả hai bên

Chấp nhận các khóa trùng lặp (trả về giá trị cuối cùng)

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

Điều này hoạt động, nhưng bạn nên thêm kiểm tra chỉ mục trước khi bắt đầu vào chuỗi con, vì có khả năng, hàng đó không chứa '='. Nguyên nhân nào gây ra ngoại lệ.
Taurib

1
Cảm ơn @Taurib vì sự giúp đỡ, đã thay đổi
Wagner Pereira

1
Cảnh báo: điều này sẽ không hoạt động nếu có các mảng trong truy vấn vì từ điển được đặt <string, string>! (ví dụ: "? item = 1 & item = 2") Workaraound: sử dụng IEnumerable <KeyValuePair <string, string >> hoặc Dictionary <string, StringValues> cho .net core 3.1
theCuriousOne

Cảm ơn @theCuriousOne, trong quy trình này, hãy trả về giá trị cuối cùng, "Chấp nhận các khóa trùng lặp (trả về giá trị cuối cùng)" vì đơn giản, giải pháp của bạn là ok để trả lại tất cả các giá trị.
Wagner Pereira

1

Điều quan trọng cần lưu ý là trong khoảng thời gian kể từ khi câu trả lời hàng đầu được gắn cờ là đúng Microsoft.AspNetCore.WebUtilitiesđã có bản cập nhật phiên bản lớn (từ 1.xx lên 2.xx).

Điều đó nói rằng, nếu bạn đang xây dựng chống lại netcoreapp1.1bạn sẽ cần chạy phần sau, cài đặt phiên bản được hỗ trợ mới nhất 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2


1

Tôi sử dụng phương pháp này làm phương pháp mở rộng, hoạt động với bất kỳ số lượng tham số nào:

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

Liên kết tiếp theo và liên kết trước, ví dụ trong một chế độ xem:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.