C# – Invio di stringhe di query con HttpClient

C# – Invio di stringhe di query con HttpClient

Le stringhe di query iniziano con "?" e hanno una o più coppie chiave-valore separate da "&". Tutti i caratteri tranne a-z, A-Z, 0-9 devono essere codificati, inclusi i caratteri Unicode.

Quando usi HttpClient, codifica automaticamente l'URI per te (internamente, delega questa attività alla classe Uri). Ciò significa che quando includi una stringa di query nell'URI, non devi codificarla da solo.

Ecco un esempio di invio di una richiesta con una stringa di query:

await httpClient.GetAsync("https://localhost:12345/movies/search?name=John Wick&hasWords=Ελληνικά");
Code language: C# (cs)

Ecco la richiesta effettiva:

GET https://localhost:12345/movies/search?name=John%20Wick&hasWords=%CE%95%CE%BB%CE%BB%CE%B7%CE%BD%CE%B9%CE%BA%CE%AC HTTP/1.1Code language: plaintext (plaintext)

Ha codificato "John Wick" come "John%20Wick" e Ελληνικά, che è composto da caratteri Unicode nell'alfabeto greco, come "%CE%95%CE%BB%CE%BB%CE%B7%CE%BD% CE%B9%CE%BA%CE%AC'.

Puoi codificare la stringa di query come mostrato sopra o compilarla. Il modo più semplice per creare la stringa di query consiste nell'utilizzare QueryHelpers.AddQueryString() (da Microsoft.AspNetCore.WebUtilities). Mostrerò come usarlo di seguito e come creare il tuo generatore di stringhe di query.

Creazione di una stringa di query con QueryHelpers.AddQueryString()

Ecco un esempio di utilizzo di QueryHelpers per creare una stringa di query:

using Microsoft.AspNetCore.WebUtilities;

var query = new Dictionary<string, string>()
{
	["name"] = "Dune",
	["year"] = "2021"
};

var uri = QueryHelpers.AddQueryString("https://localhost:12345/movies/search", query);

var result = await httpClient.GetAsync(uri);
Code language: C# (cs)

Ecco la richiesta vera e propria:

GET https://localhost:12345/movies/search?name=Dune&year=2021 HTTP/1.1Code language: plaintext (plaintext)

Ottieni Microsoft.AspNetCore.WebUtilities

QueryHelpers si trova nel pacchetto Microsoft.AspNetCore.WebUtilities. Se non stai lavorando su un progetto ASP.NET Core, puoi aggiungere il pacchetto (Visualizza> Altre finestre> Console di gestione pacchetti) :

Install-Package Microsoft.AspNetCore.WebUtilities
Code language: PowerShell (powershell)

Questo è disponibile per .NET Standard 2.0, il che significa che funziona in:.NET Core 1.0+, .NET Framework 4.6.1+, .NET 5+, Unity 2018.1+ e altro.

Cosa succede quando passi un URI codificato a HttpClient?

QueryHelpers.AddQueryString() formatta e codifica la stringa di query. Poiché HttpClient codifica già l'URI, potresti chiederti cosa succede se gli passi una stringa di query codificata? Non preoccuparti, non lo codificherà due volte.

Ecco un esempio di passaggio di una stringa di query codificata:

await httpClient.GetAsync("https://localhost:12345/movies/search?name=John%20Wick&hasWords=%CE%95%CE%BB%CE%BB%CE%B7%CE%BD%CE%B9%CE%BA%CE%AC")
Code language: C# (cs)

Lo invierà così com'è, poiché l'URI è già codificato. Ecco la richiesta effettiva:

GET https://localhost:12345/movies/search?name=John%20Wick&hasWords=%CE%95%CE%BB%CE%BB%CE%B7%CE%BD%CE%B9%CE%BA%CE%AC HTTP/1.1Code language: plaintext (plaintext)

Utilizzo del proprio generatore di stringhe di query

Diciamo che non vuoi aggiungere il pacchetto Microsoft.AspNetCore.WebUtilities, o forse vuoi personalizzare il modo in cui vengono create le stringhe di query. In tal caso, puoi utilizzare il tuo generatore di stringhe di query, utilizzando il codice sorgente QueryHelper.AddQueryString() come punto di partenza.

Ecco una versione ridotta con la codifica rimossa:

public static class RequestUriUtil
{
	public static string GetUriWithQueryString(string requestUri, 
		Dictionary<string, string> queryStringParams)
	{
		bool startingQuestionMarkAdded = false;
		var sb = new StringBuilder();
		sb.Append(requestUri);
		foreach (var parameter in queryStringParams)
		{
			if (parameter.Value == null)
			{
				continue;
			}

			sb.Append(startingQuestionMarkAdded ? '&' : '?');
			sb.Append(parameter.Key);
			sb.Append('=');
			sb.Append(parameter.Value);
			startingQuestionMarkAdded = true;
		}
		return sb.ToString();
	}
}
Code language: C# (cs)

QueryHelpers codifica le chiavi/valori (es:UrlEncoder.Default.Encode(parameter.Key)), mentre questo codice esegue solo la formattazione. Ricorda che HttpClient lo codificherà automaticamente, quindi non è necessario codificarlo qui.

Ecco un esempio di utilizzo di questo generatore di stringhe di query:

var query = new Dictionary<string, string>()
{
	["name"] = "John Wick",
	["year"] = "2014",
	["hasWords"] = "Ελληνικά"
};

var requestUriWithQuery = RequestUriUtil.GetUriWithQueryString("https://localhost:12345/movies/search", query);

var result = await httpClient.GetAsync(requestUriWithQuery);
Code language: C# (cs)

Ecco la richiesta vera e propria:

GET https://localhost:12345/movies/search?name=John%20Wick&year=2014&hasWords=%CE%95%CE%BB%CE%BB%CE%B7%CE%BD%CE%B9%CE%BA%CE%AC HTTP/1.1Code language: plaintext (plaintext)

Metodo di estensione HttpClient GetWithQueryStringAsync()

Se stai utilizzando HttpClient e un generatore di stringhe di query, potresti voler utilizzare un metodo di estensione per semplificare il codice chiamante (e potenzialmente rimuovere la duplicazione):

using Microsoft.AspNetCore.WebUtilities;

public static class HttpClientExtensions
{
	public static async Task<HttpResponseMessage> GetWithQueryStringAsync(this HttpClient client, string uri, 
		Dictionary<string, string> queryStringParams)
	{
		var url = QueryHelpers.AddQueryString(uri, queryStringParams);

		return await client.GetAsync(url);
	}
}
Code language: C# (cs)

Usalo in questo modo:

var query = new Dictionary<string, string>()
{
	["name"] = "Dune",
	["year"] = "2021"
};

var result = await httpClient.GetWithQueryStringAsync("https://localhost:12345/movies/search", query);
Code language: C# (cs)

HttpClient ha molti metodi con molti overload, quindi puoi usarlo come punto di partenza per aggiungere wrapper per qualsiasi metodo tu stia usando.

Ciò è particolarmente utile se stai utilizzando il tuo generatore di stringhe di query, perché puoi renderlo un metodo privato utilizzato solo dai metodi di estensione HttpClient:

public static class HttpClientExtensions
{
	public static async Task<HttpResponseMessage> GetWithQueryStringAsync(this HttpClient client, string uri,
		Dictionary<string, string> queryStringParams)
	{
		var url = GetUriWithQueryString(uri, queryStringParams);

		return await client.GetAsync(url);
	}

	private static string GetUriWithQueryString(string requestUri,
			Dictionary<string, string> queryStringParams)
	{
		bool startingQuestionMarkAdded = false;
		var sb = new StringBuilder();
		sb.Append(requestUri);
		foreach (var parameter in queryStringParams)
		{
			if (parameter.Value == null)
			{
				continue;
			}

			sb.Append(startingQuestionMarkAdded ? '&' : '?');
			sb.Append(parameter.Key);
			sb.Append('=');
			sb.Append(parameter.Value);
			startingQuestionMarkAdded = true;
		}
		return sb.ToString();
	}
}
Code language: C# (cs)