HttpRequestMessage.Content теряется, когда он читается в logging DelegatingHandler в ASP.Net Web API
При попытке объекта в Action в контроллере он спорадически кажется нулевым. Я обнаружил, что это связано с ReadAsStringAsync()
в SendAsync()
переопределении DelegatingHandler
. Проблема заключается в содержании. Когда мой клиент отправляет тело контента и он читается в журнале, он никогда не читается Invoker контроллера (или может быть где-то в JsonFormatter
). Я подозреваю, что последующий вызов Content.ReadAsStringAsync()
не генерирует исключение, но также не возвращает ожидаемое тело содержимого (возвращается некоторая информация, указывающая, что чтение асинхронного текста завершено).
Но моя проблема остается, так как я хочу прочитать параметр [FromBody]
в действии, и он равен нулю, когда RaceCondition Content.ReadStringAsync
выигрывает DelegatingHandler
. Однако, когда JsonFormatter
выигрывает, я получаю объект, но это редко (только при запуске службы).
Вот мой код DelegatingHandler
:
public class LogHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var apiRequest = new WebApiUsageRequest(request);
WriteLog(apiRequest);
request.Content.ReadAsStringAsync().ContinueWith(t =>
{
apiRequest.Content = t.Result;
WriteLog(apiRequest);
});
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var apiResponse = new WebApiUsageResponse(task.Result);
apiResponse.Content = task.Result.Content != null ? task.Result.Content.ReadAsStringAsync().Result : null;
WriteLog(apiResponse);
return task.Result;
});
}
}
У кого-нибудь есть ключ к решению этой проблемы?
Ответы
Ответ 1
Это по дизайну. В ASP.NET Web API содержимое тела обрабатывается только для прямого потока, который может быть прочитан только один раз.
Вы можете попробовать использовать трафик веб-API ASP.NET, но я еще не тестировал его с запросом POST, поэтому я не уверен, как/если он отслеживает тело запроса (он отслеживает параметры для запроса GET). Вы можете прочитать здесь:
Ответ 2
Метод ReadAsStreamAsync возвращает содержимое тела.
var body = string.Empty;
using (var reader = new StreamReader(request.Content.ReadAsStreamAsync().Result))
{
reader.BaseStream.Seek(0, SeekOrigin.Begin);
body = reader.ReadToEnd();
}
Ответ 3
Вот что я в итоге сделал:
public string SafelyReadContent(HttpRequestMessage request)
{
var stream = request.Content.ReadAsStreamAsync().Result;
var reader = new StreamReader(stream);
var result = reader.ReadToEnd();
stream.Seek(0, SeekOrigin.Begin);
return result;
}
@pirimoglu ответ использования блока "using" не работал у меня, поскольку, когда читатель был удален, основной поток также был закрыт.
Ответ 4
но если вы используете код ниже в SendAsync, он работает правильно
if (request.Content != null)
{
request.Content.ReadAsByteArrayAsync().ContinueWith
(
(task) =>
{
var xxx = System.Text.UTF8Encoding.UTF8.GetString(task.Result);
});
}
return base.SendAsync(request, cancellationToken) //than call the base
.
,
.
Ответ 5
Это сработало для меня:
using (var stream = new MemoryStream())
{
var context = (HttpContextBase)Request.Properties["MS_HttpContext"];
context.Request.InputStream.Seek(0, SeekOrigin.Begin);
context.Request.InputStream.CopyTo(stream);
string requestBody = Encoding.UTF8.GetString(stream.ToArray());
}
Вернул для меня json-представление объекта моего параметра, поэтому я мог использовать его для обработки исключений и регистрации.
Найдено как принятый ответ здесь
Ответ 6
Я получаю эту ошибку
Доступ к BinaryRead, Form, Files или InputStream осуществлялся до того, как вызывающая сторона HttpRequest.GetBufferedInputStream заполнила внутреннее хранилище.