Отменить HttpRequestMessage - Исключение
Я хочу отправить один и тот же запрос более одного раза, например:
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);
Отправка запроса во второй раз вызовет исключение с сообщением:
Сообщение запроса уже отправлено. Не удается отправить тот же запрос сообщение несколько раз.
Является ли их способ "клонировать" запрос, чтобы я мог отправить его снова?
Мой реальный код имеет больше переменных, установленных в HttpRequestMessage
, чем в приведенном выше примере, такие переменные, как заголовки и метод запроса.
Ответы
Ответ 1
Я написал следующий метод расширения для клонирования запроса.
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;
}
Ответ 2
Я передаю экземпляр Func<HttpRequestMessage>
вместо экземпляра HttpRequestMessage
. Функция func указывает на метод factory, поэтому я получаю совершенно новое сообщение каждый раз, когда он вызывается вместо повторного использования.
Ответ 3
Вот усовершенствование метода расширения, предложенного @drahcir. Улучшение заключается в обеспечении клонирования содержимого запроса, а также самого запроса:
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;
}
Ответ 4
AFAIK, HttpClient - это всего лишь обертка вокруг "HttpWebRequest", которая использует потоки для отправки/получения данных, что делает невозможным повторное использование запроса, хотя это должно быть довольно просто, чтобы просто клонировать его/сделать это в цикле.
Ответ 5
У меня есть аналогичная проблема и разрешила это с помощью взлома, отражения.
Спасибо за открытый исходный код! Читая исходный код, в нем есть частное поле _sendStatus
в HttpRequestMessage
, то, что я сделал, это reset it до 0
, прежде чем повторно использовать сообщение запроса. Он работает в .NET Core, и я хочу, чтобы Microsoft не переименовывала и не удаляла его навсегда.: Р
// 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.");
}