Не удалось прочитать входной поток

Я использую ActionFilterAttribute чтобы получить запрос до нажатия на контроллер, как показано ниже:

 public override void OnActionExecuting(HttpActionContext actionContext)
 {
     using (var stream = new MemoryStream())
     {
        HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
        context.Request.InputStream.Seek(0, SeekOrigin.Begin);
        context.Request.InputStream.CopyTo(stream);
        requestBody = Encoding.UTF8.GetString(stream.ToArray());
     }
 }

Вышеуказанный метод работает для небольшого запроса, но для большого json он дает мне эту ошибку:

Доступ к BinaryRead, Form, Files или InputStream осуществлялся до того, как внутренняя память была заполнена вызывающим объектом HttpRequest.GetBufferedInputStream.

И входной поток дает эту ошибку

context.Request.InputStream выбрасывает исключение типа System.InvalidOperationException System.IO.Stream {System.InvalidOperationException}

Как я нашел в своих исследованиях, что это проблема с таймаутом, но я не могу изменить таймаут в коде. Я попытался изменить значения в файле web.config maxRequestLength="102400000" и maxAllowedContentLength="209715100" но все же я столкнулся с той же ошибкой.
Если я прочитаю GetBufferedInputStream но все еще ту же проблему, он читает только часть буфера, а не весь поток.

Я также попробовал следующее:

 Stream InStream;
 int Len;
 InStream = HttpContext.Current.Request.InputStream;
 Len = System.Convert.ToInt32(InStream.Length);
 byte[] ByteArray = new byte[Len + 1];
 InStream.Seek(0, SeekOrigin.Begin);
 InStream.Read(ByteArray, 0, Len);
 var jsonParam = System.Text.Encoding.UTF8.GetString(ByteArray); 

Обратите внимание: если я установил application/xml типа контента application/xml или application/x-www-form-urlencoded оно будет работать, но если я его установлю в application/json он даст мне эту ошибку!

Пожалуйста, порекомендуйте!

Ответы

Ответ 1

Есть несколько моментов:

Во-первых, если вы попытаетесь прочитать 0 байт из потока, тогда он выкинет исключение System.InvalidOperationException. Итак, я изменю свой код, как ContentLength > 0 ниже, и добавьте проверку ContentLength > 0.

using (var stream = new MemoryStream())
     {
        HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
        if(context.Request.Contentlength > 0)
        {
            context.Request.InputStream.Seek(0, SeekOrigin.Begin);
            context.Request.InputStream.CopyTo(stream);
            requestBody = Encoding.UTF8.GetString(stream.ToArray());
        }
     }

Кроме того, я однажды испытал ту же проблему, и увеличение maxRequestLength в web.config, похоже, решило проблему. Эта ссылка дает дополнительную информацию здесь

Ответ 2

Вот как я это делаю внутри моего связующего объекта Model, но я не уверен, как он будет работать с вашим фильтром Action. Я проверил в Интернете и там противоречивую информацию; Некоторые говорят, что вы не можете прочитать входной поток, так как он не доступен для поиска, и ASP.NET должен будет прочитать его для привязки модели. Некоторые говорят, что он действительно доступен для поиска, и используйте метод, который вы использовали выше. Таким образом, единственный способ выяснить, что действительно работает, - проверить.

Надеюсь, мой образец кода поможет вам разобраться.

object request = null;
if (actionContext.Request.Method == HttpMethod.Post && "application/json".Equals(actionContext.Request.Content.Headers.ContentType.MediaType))
{
    var jsonContentTask = actionContext.Request.Content.ReadAsStringAsync();
    Task.WaitAll(jsonContentTask);
    string jsonContent = jsonContentTask.Result;
    //... other stuff
}

Ответ 3

Возможно, я ошибаюсь, но вот что я нашел. Фильтры действий выполняются после привязки модели, что означает, что поток запросов уже был прочитан. В вашем случае я не уверен, что это значит. https://exceptionnotfound.net/the-asp-net-web-api-2-http-message-lifecycle-in-43-easy-steps-2/ подробно объясняет жизненный цикл. Изменение типа контента не изменило бы события жизненного цикла, но скорее изменило бы содержимое запроса, которое, в свою очередь, могло бы повлиять на привязку модели. Если у вас есть набор моделей для действия, тогда вам следует помочь как получить текущую модель в фильтре действий. Таким образом, решение было бы получить объект модели из actionContext и затем соответствующим образом изменить его.