Parse dan ubah string kueri di .NET Core

113

Saya diberi URI absolut yang berisi string kueri. Saya ingin menambahkan nilai ke string kueri dengan aman, dan mengubah parameter yang ada.

Saya lebih suka untuk tidak &foo=barmenggunakan, atau menggunakan ekspresi reguler, pelolosan URI itu rumit. Sebaliknya saya ingin menggunakan mekanisme built-in yang saya tahu akan melakukan ini dengan benar dan menangani pelarian.

Saya telah menemukan sebuah ton dari jawaban bahwa semua penggunaan HttpUtility. Namun ini menjadi ASP.NET Core, tidak ada lagi perakitan System.Web lagi, jadi tidak ada lagi HttpUtility.

Apa cara yang tepat untuk melakukan ini di ASP.NET Core sambil menargetkan runtime inti?

vcsjones.dll
sumber
Alternatif untuk Microsoft.AspNet.WebUtiltiesmungkin Mono.HttpUtilityperpustakaan .
tukang
Saya menulis posting yang sama, lihat di sini: neelbhatt40.wordpress.com/2017/07/06/…
Neel
2
Pembaruan 2017: .NET Core 2.0 sekarang termasuk HttpUtilitydan ParseQueryStringmetode.
KTCO

Jawaban:

152

Jika Anda menggunakan ASP.NET Inti 1 atau 2, Anda dapat melakukan ini dengan Microsoft.AspNetCore.WebUtilities.QueryHelpersdi Microsoft.AspNetCore.WebUtilities paket.

Jika Anda menggunakan ASP.NET Core 3.0 atau yang lebih baru, WebUtilitiessekarang menjadi bagian dari ASP.NET SDK dan tidak memerlukan referensi paket nuget terpisah.

Untuk menguraikannya menjadi kamus:

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

Perhatikan bahwa tidak seperti ParseQueryStringdi System.Web, ini mengembalikan jenis kamus IDictionary<string, string[]>di ASP.NET Core 1.x, atau IDictionary<string, StringValues>di ASP.NET Core 2.x atau lebih tinggi, jadi nilainya adalah kumpulan string. Beginilah cara kamus menangani beberapa parameter string kueri dengan nama yang sama.

Jika Anda ingin menambahkan parameter ke string kueri, Anda dapat menggunakan metode lain di 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);

Menggunakan .net core 2.2 Anda bisa mendapatkan string kueri menggunakan

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

Anda akan mendapatkan kumpulan pasangan kunci: nilai - seperti ini

[0] {[companyName, ]}
[1] {[shop, ]}
[2] {[breath, ]}
[3] {[hand, ]}
[4] {[eye, ]}
[5] {[firstAid, ]}
[6] {[eyeCleaner, ]}
vcsjones.dll
sumber
1
FYI, paket WebUtilities tidak kompatibel dengan .net core 1.0. Anda mungkin membutuhkannya Microsoft.AspNetCore.WebUtilities.
Jaime
6
@Jaime Pengamatan yang luar biasa! Dapatkah Anda mengedit jawaban saya dengan pembaruan itu sehingga Anda mendapat pujian untuk itu?
vcsjones
3
Edisi selesai. Juga menyimpan namespace lama untuk kasus versi .net yang lama.
Jaime
1
Tampaknya menggunakan QueryHelpers.AddQueryString akan secara otomatis UrlEscape string - berguna.
Josh
2
Jenis kembalian sekarang adalah IDictionary <string, StringValues> daripada IDictionary <string, string []>
btlog
35

Cara termudah dan paling intuitif untuk mengambil URI absolut dan memanipulasi string kuerinya hanya menggunakan paket ASP.NET Core, dapat dilakukan dalam beberapa langkah mudah:

Instal Paket

PM> Instal-Paket Microsoft.AspNetCore.WebUtilities
PM> Instal-Paket Microsoft.AspNetCore.Http.Extensions

Kelas Penting

Hanya untuk menunjukkannya, berikut adalah dua kelas penting yang akan kita gunakan: QueryHelpers , StringValues , QueryBuilder .

Kode

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

Untuk tetap mengetahui perubahan apa pun, Anda dapat melihat posting blog saya tentang ini di sini: http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/

Ben Cull
sumber
17

HttpRequestmemiliki Queryproperti yang mengekspos string kueri yang diurai melalui IReadableStringCollectionantarmuka:

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

Diskusi di GitHub ini juga menunjukkannya.

Yuval Itzchakov
sumber
10

Fungsi ini kembali Dictionary<string, string>dan tidak digunakan Microsoft.xxxuntuk kompatibilitas

Menerima pengkodean parameter di kedua sisi

Menerima kunci duplikat (kembalikan nilai terakhir)

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;
}
Wagner Pereira
sumber
Ini berfungsi, tetapi Anda harus menambahkan pemeriksaan indeks sebelum memulai substring, karena ada kemungkinan, baris itu tidak berisi '='. Yang menyebabkan pengecualian.
Taurib
1
Terima kasih @Taurib atas bantuannya, diubah
Wagner Pereira
1
Peringatan: ini tidak akan berfungsi jika ada array dalam kueri karena kamus disetel <string, string>! (misalnya "? item = 1 & item = 2") Solusi: gunakan IEnumerable <KeyValuePair <string, string >> atau Dictionary <string, StringValues> untuk .net core 3.1
theCuriousOne
Terima kasih @theCuriousOne, dalam rutinitas ini, kembalikan nilai terakhir, "Menerima kunci duplikat (kembalikan nilai terakhir)" untuk kesederhanaan, solusi Anda baik untuk mengembalikan semua nilai.
Wagner Pereira
1

Penting untuk dicatat bahwa pada waktu karena jawaban teratas telah ditandai sebagai benar yang Microsoft.AspNetCore.WebUtilitiesmemiliki pembaruan versi utama (dari 1.xx ke 2.xx).

Karena itu, jika Anda membangunnya, netcoreapp1.1Anda perlu menjalankan perintah berikut, yang menginstal versi terbaru yang didukung 1.1.2:

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2

pimbrouwers
sumber
1

Saya menggunakan ini sebagai metode ekstensi, bekerja dengan sejumlah parameter:

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

Tautan berikutnya dan sebelumnya misalnya dalam tampilan:

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

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
Gabriel Luca
sumber