postasync ejemplos consumir c# .net dotnet-httpclient

c# - ejemplos - Re-Send HttpRequestMessage-Exception



httpclient c# post json (5)

Quiero enviar la misma solicitud más de una vez, por ejemplo:

HttpClient client = new HttpClient(); HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, "http://example.com"); await client.SendAsync(req, HttpCompletionOption.ResponseContentRead); await client.SendAsync(req, HttpCompletionOption.ResponseContentRead);

Enviar la solicitud por segunda vez arrojará una excepción con el mensaje:

El mensaje de solicitud ya fue enviado. No se puede enviar el mismo mensaje de solicitud varias veces.

¿Es una forma de " clonar " la solicitud para que pueda enviarla de nuevo?

Mi código real tiene más variables configuradas en HttpRequestMessage que en el ejemplo anterior, variables como encabezados y método de solicitud.


AFAIK, HttpClient es solo un envoltorio alrededor de ''HttpWebRequest''s, que utiliza flujos para enviar / recibir datos, lo que hace imposible reutilizar la solicitud, aunque debería ser bastante simple juste clonarla / hacer esto en un ciclo.


Aquí hay una mejora del método de extensión propuesto por @drahcir. La mejora es garantizar que el contenido de la solicitud se clone, así como la solicitud en sí:

public static HttpRequestMessage Clone(this HttpRequestMessage request) { var clone = new HttpRequestMessage(request.Method, request.RequestUri) { Content = request.Content.Clone(), Version = request.Version }; foreach (KeyValuePair<string, object> prop in request.Properties) { clone.Properties.Add(prop); } foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers) { clone.Headers.TryAddWithoutValidation(header.Key, header.Value); } return clone; } public static HttpContent Clone(this HttpContent content) { if (content == null) return null; var ms = new MemoryStream(); content.CopyToAsync(ms).Wait(); ms.Position = 0; var clone = new StreamContent(ms); foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers) { clone.Headers.Add(header.Key, header.Value); } return clone; }

Edición 05/02/18 : aquí está la versión asíncrona

public static Task<HttpRequestMessage> CloneAsync(this HttpRequestMessage request) { var clone = new HttpRequestMessage(request.Method, request.RequestUri) { Content = await request.Content.CloneAsync().ConfigureAwait(false), Version = request.Version }; foreach (KeyValuePair<string, object> prop in request.Properties) { clone.Properties.Add(prop); } foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers) { clone.Headers.TryAddWithoutValidation(header.Key, header.Value); } return clone; } public static Task<HttpContent> CloneAsync(this HttpContent content) { if (content == null) return null; var ms = new MemoryStream(); await content.CopyToAsync(ms).ConfigureAwait(false); ms.Position = 0; var clone = new StreamContent(ms); foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers) { clone.Headers.Add(header.Key, header.Value); } return clone; }


Escribí el siguiente método de extensión para clonar la solicitud.

public static HttpRequestMessage Clone(this HttpRequestMessage req) { HttpRequestMessage clone = new HttpRequestMessage(req.Method, req.RequestUri); clone.Content = req.Content; clone.Version = req.Version; foreach (KeyValuePair<string, object> prop in req.Properties) { clone.Properties.Add(prop); } foreach (KeyValuePair<string, IEnumerable<string>> header in req.Headers) { clone.Headers.TryAddWithoutValidation(header.Key, header.Value); } return clone; }


Estoy pasando una instancia de Func<HttpRequestMessage> lugar de una instancia de HttpRequestMessage . La función apunta a un método de fábrica, así que recibo un mensaje nuevo cada vez que se llama en lugar de reutilizarlo.


Tengo un problema similar y lo resolví de forma hacker, reflejo.

Gracias por el código abierto! Al leer el código fuente, resulta que hay un campo privado _sendStatus en la clase HttpRequestMessage , lo que hice fue restablecerlo a 0 antes de reutilizar el mensaje de solicitud. Funciona en .NET Core y deseo que Microsoft no cambie su nombre ni lo elimine para siempre. :pag

// using System.Reflection; // using System.Net.Http; // private const string SEND_STATUS_FIELD_NAME = "_sendStatus"; private void ResetSendStatus(HttpRequestMessage request) { TypeInfo requestType = request.GetType().GetTypeInfo(); FieldInfo sendStatusField = requestType.GetField(SEND_STATUS_FIELD_NAME, BindingFlags.Instance | BindingFlags.NonPublic); if (sendStatusField != null) sendStatusField.SetValue(request, 0); else throw new Exception($"Failed to hack HttpRequestMessage, {SEND_STATUS_FIELD_NAME} doesn''t exist."); }