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 заполнила внутреннее хранилище.