C#:envío de cadenas de consulta con HttpClient

C#:envío de cadenas de consulta con HttpClient

Las cadenas de consulta comienzan con '?' y tienen uno o más pares clave-valor separados por '&'. Todos los caracteres excepto a-z, A-Z, 0-9 deben estar codificados, incluidos los caracteres Unicode.

Cuando usa HttpClient, codifica automáticamente el URI por usted (internamente, delega esta tarea a la clase Uri). Esto significa que cuando incluye una cadena de consulta en el URI, no necesita codificarla usted mismo.

Este es un ejemplo de envío de una solicitud con una cadena de consulta:

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

Aquí está la solicitud real:

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)

Codificó 'John Wick' como 'John%20Wick', y Ελληνικά, que se compone de caracteres Unicode en el alfabeto griego, como '%CE%95%CE%BB%CE%BB%CE%B7%CE%BD% CE%B9%CE%BA%CE%AC'.

Puede codificar la cadena de consulta como se muestra arriba o compilarla. La forma más sencilla de crear la cadena de consulta es usar QueryHelpers.AddQueryString() (de Microsoft.AspNetCore.WebUtilities). Mostraré cómo usarlo a continuación y cómo crear su propio generador de cadenas de consulta.

Crear una cadena de consulta con QueryHelpers.AddQueryString()

Este es un ejemplo del uso de QueryHelpers para crear una cadena de consulta:

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)

Esta es la solicitud real:

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

Obtener Microsoft.AspNetCore.WebUtilities

QueryHelpers está en el paquete Microsoft.AspNetCore.WebUtilities. Si no está trabajando en un proyecto de ASP.NET Core, puede agregar el paquete (Ver> Otras ventanas> Consola del administrador de paquetes) :

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

Está disponible para .NET Standard 2.0, lo que significa que funciona en:.NET Core 1.0+, .NET Framework 4.6.1+, .NET 5+, Unity 2018.1+ y más.

¿Qué sucede cuando pasa un URI codificado a HttpClient?

QueryHelpers.AddQueryString() formatea y codifica la cadena de consulta. Dado que HttpClient ya codifica el URI, es posible que se pregunte qué sucede si le pasa una cadena de consulta codificada. No se preocupe, no lo codificará dos veces.

Aquí hay un ejemplo de pasar una cadena de consulta codificada:

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 enviará tal cual, ya que el URI ya está codificado. Aquí está la solicitud real:

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)

Usar su propio generador de cadenas de consulta

Supongamos que no desea agregar el paquete Microsoft.AspNetCore.WebUtilities, o tal vez desea personalizar cómo se crean las cadenas de consulta. En ese caso, puede usar su propio generador de cadenas de consulta, usando el código fuente QueryHelper.AddQueryString() como punto de partida.

Aquí hay una versión simplificada con la codificación eliminada:

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 las claves/valores (por ejemplo, UrlEncoder.Default.Encode(parameter.Key)), mientras que este código solo realiza el formateo. Recuerde que HttpClient lo codificará automáticamente, por lo que no es necesario codificarlo aquí.

Este es un ejemplo del uso de este generador de cadenas de consulta:

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)

Esta es la solicitud real:

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)

método de extensión HttpClient GetWithQueryStringAsync()

Si está utilizando HttpClient y un generador de cadenas de consulta, es posible que desee utilizar un método de extensión para simplificar el código de llamada (y potencialmente eliminar la duplicación):

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)

Úselo así:

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 tiene muchos métodos con muchas sobrecargas, por lo que puede usar esto como punto de partida para agregar envoltorios para cualquier método que esté usando.

Esto es especialmente útil si está utilizando su propio generador de cadenas de consulta, porque puede convertirlo en un método privado que solo usan los métodos de extensión 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)