Обратный поток тела запроса назад
Я повторно использую регистратор запросов как Owin Middleware, который регистрирует URL-адрес запроса и тело всех входящих запросов. Я могу прочитать тело, но если я делаю параметр body в моем контроллере, то он равен нулю.
Я предполагаю, что это null, потому что позиция потока находится в конце, поэтому нет ничего, что можно было бы прочитать, когда он пытается десериализовать тело. У меня была аналогичная проблема в предыдущей версии Web 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
}
}
Ответы
Ответ 1
Просто найдено одно решение. Замена исходного потока новым потоком, содержащим данные.
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
также будет работать как замена.
Ответ 2
Здесь небольшое улучшение в первом ответе Despertar, которое мне очень помогло, но я столкнулся с проблемой при работе с двоичными данными. Промежуточный шаг извлечения потока в строку и затем вернув его в массив байтов, используя 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
хорош, потому что вы можете проверить длину потока перед записью всего тела (что я не хочу делать, если кто-то загружает огромный файл).
Ответ 3
Я знаю, что это старо, но только для того, чтобы помочь любому, кто сталкивается с этим.
Вам нужно искать в потоке: context.Request.Body.Seek(0, SeekOrigin.Begin);