2014-02-15 2 views
24

Я перепрограммирую регистратор запросов как промежуточное ПО Owin, которое регистрирует URL-адрес запроса и тело всех входящих запросов. Я могу прочитать тело, но если я делаю параметр body в моем контроллере, то null.Обратный поток тела запроса назад

Я предполагаю, что это нулевое значение, потому что позиция потока находится в конце, поэтому нет ничего, что можно было бы прочитать, когда он попытается десериализовать тело. У меня была аналогичная проблема в предыдущей версии веб-API, но я смог установить позицию Stream обратно в 0. Этот конкретный поток генерирует исключение This stream does not support seek operations.

В последней версии Web API 2.0 я мог позвонить Request.HttpContent.ReadAsStringAsync() внутри моего регистратора запросов, и тело по-прежнему будет поступать на контроллер в такт.

Как перемотать поток после прочтения?

или

Как я могу прочитать тело запроса, не потребляя его?

public class RequestLoggerMiddleware : OwinMiddleware 
{ 
    public RequestLoggerMiddleware(OwinMiddleware next) 
     : base(next) 
    { 
    } 

    public override Task Invoke(IOwinContext context) 
    { 
     return Task.Run(() => { 
      string body = new StreamReader(context.Request.Body).ReadToEnd(); 
      // log body 

      context.Request.Body.Position = 0; // cannot set stream position back to 0 
      Console.WriteLine(context.Request.Body.CanSeek); // prints false 
      this.Next.Invoke(context); 
     }); 
    } 
} 

public class SampleController : ApiController 
{ 
    public void Post(ModelClass body) 
    { 
     // body is now null if the middleware reads it 
    } 
} 

ответ

29

Просто нашел одно решение. Замена исходного потока нового потока, содержащего данные.

public override Task Invoke(IOwinContext context) 
    { 
     return Task.Run(() => { 
      string body = new StreamReader(context.Request.Body).ReadToEnd(); 
      // log body 

      byte[] requestData = Encoding.UTF8.GetBytes(body); 
      context.Request.Body = new MemoryStream(requestData); 
      this.Next.Invoke(context); 
     }); 
    } 

Если вы имеете дело с большими объемами данных, я уверен, что FileStream также будет работать в качестве замены.

+4

Другая альтернатива: Stream anotherStream = new MemoryStream(); context.Request.Body.CopyToAsync (anotherStream); –

+9

В ядре ASP.NET (где метод invoke имеет следующую подпись: «async Task Invoke (контекст HttpContext)»), вы можете сделать следующее, чтобы получить буферный поток, который позволяет вам искать и читать его несколько раз; 'context.Request.EnableRewind()' (метод расширения HttpRequest, найденный в 'Microsoft.AspNetCore.Http.Internal.BufferingHelper'). – jfiskvik

+0

@jfiskvik Ваш вход был очень полезен для меня. Он помог мне перемотать мой поток после обработки, которая произошла в моем MiddleWare. – ExtremeSwat

-3

Я знаю, что это старо, но только для того, чтобы помочь любому, кто сталкивается с этим. Вы должны искать в потоке: context.Request.Body.Seek(0, SeekOrigin.Begin);

+4

То же, что и 'Body.Position = 0', это вызывает« NotSupportedException »с сообщением« Этот поток не поддержка поиска операций'. Также свойство 'Body.CanSeek' возвращает false. – Despertar

4

Вот небольшое улучшение в первый ответ по Деспертар, который помог мне много, но я столкнулся с проблемой при работе с двоичными данными. Промежуточный шаг извлечения потока в строку, а затем его возврат в массив байтов с использованием Encoding.UTF8.GetBytes(body) испортил двоичный контент (содержимое изменится, если оно не является кодировкой UTF8). Вот мое исправление с помощью Stream.CopyTo():

public override Task Invoke(IOwinContext context) 
{ 
    return Task.Run(() => 
    { 
     using (var streamCopy = new MemoryStream()) 
     { 
      context.Request.Body.CopyTo(streamCopy); 
      streamCopy.Position = 0; // rewind 

      string body = new StreamReader(streamCopy).ReadToEnd(); 
      // log body 

      streamCopy.Position = 0; // rewind again 
      context.Request.Body = streamCopy; // put back in place for downstream handlers 

      this.Next.Invoke(context); 
     } 
    }); 
} 

Кроме того, MemoryStream приятно, потому что вы можете проверить длину потока, прежде чем войти в полное тело (это то, что я не хочу делать в случае, если кто-то загружает огромный файл).

Смежные вопросы