Я изо всех сил пытался получить Response.Body
свойство из действия ASP.NET Core, и единственное решение, которое я смог определить, кажется неоптимальным. Решение требует замены из Response.Body
с MemoryStream
при чтении потока в строковую переменную, а затем обменивать его обратно перед отправкой клиенту. В приведенных ниже примерах я пытаюсь получить Response.Body
значение в настраиваемом классе промежуточного программного обеспечения. по какой-то причине Response.Body
является свойством только для набора в ASP.NET Core? Я просто что-то упустил или это проблема недосмотра / ошибки / дизайна? Есть ли лучший способ читать Response.Body
?
Текущее (неоптимальное) решение:
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
using (var swapStream = new MemoryStream())
{
var originalResponseBody = context.Response.Body;
context.Response.Body = swapStream;
await _next(context);
swapStream.Seek(0, SeekOrigin.Begin);
string responseBody = new StreamReader(swapStream).ReadToEnd();
swapStream.Seek(0, SeekOrigin.Begin);
await swapStream.CopyToAsync(originalResponseBody);
context.Response.Body = originalResponseBody;
}
}
}
Попытка решения с использованием EnableRewind (): работает только для Request.Body
, но не для Response.Body
. Это приводит к чтению пустой строки, Response.Body
а не фактического содержимого тела ответа.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifeTime)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.Use(async (context, next) => {
context.Request.EnableRewind();
await next();
});
app.UseMyMiddleWare();
app.UseMvc();
// Dispose of Autofac container on application stop
appLifeTime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());
}
MyMiddleWare.cs
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await _next(context);
string responseBody = new StreamReader(context.Request.Body).ReadToEnd(); //responseBody is ""
context.Request.Body.Position = 0;
}
}
В моем первоначальном ответе я совершенно неправильно понял вопрос и подумал, что плакат спрашивает, как читать. Request.Body
Но он спросил, как читать Response.Body
. Я оставляю свой исходный ответ, чтобы сохранить историю, но также обновляю его, чтобы показать, как я отвечу на вопрос, прочитав его правильно.
Оригинальный ответ
Если вам нужен буферизованный поток, поддерживающий многократное чтение, вам нужно установить
context.Request.EnableRewind()
В идеале это нужно делать на раннем этапе промежуточного программного обеспечения, прежде чем что-либо понадобится для чтения тела.
Так, например, вы можете поместить следующий код в начало Configure
метода файла Startup.cs:
app.Use(async (context, next) => {
context.Request.EnableRewind();
await next();
});
Перед включением перемотки назад поток, связанный с, Request.Body
является потоком только вперед, который не поддерживает поиск или чтение потока во второй раз. Это было сделано для того, чтобы сделать стандартную конфигурацию обработки запросов как можно более легкой и производительной. Но как только вы включаете перемотку назад, поток обновляется до потока, который поддерживает поиск и чтение несколько раз. Вы можете наблюдать это «обновление», установив точку останова непосредственно перед и сразу после вызова EnableRewind
и наблюдая за Request.Body
свойствами. Так, например Request.Body.CanSeek
, изменится с false
на true
.
обновление : Request.EnableBuffering()
доступен запуск в ASP.NET Core 2.1, который обновляет его Request.Body
до FileBufferingReadStream
подобного, Request.EnableRewind()
и, поскольку он Request.EnableBuffering()
находится в общедоступном пространстве имен, а не во внутреннем, его следует предпочесть EnableRewind (). (Спасибо @ArjanEinbu за указание)
Затем, чтобы прочитать поток тела, вы могли бы, например, сделать это:
string bodyContent = new StreamReader(Request.Body).ReadToEnd();
Не оборачивайте StreamReader
создание в операторе using, иначе он закроет базовый поток тела по завершении блока using, и код позже в жизненном цикле запроса не сможет прочитать тело.
Также на всякий случай было бы неплохо выполнить приведенную выше строку кода, которая считывает содержимое тела, с этой строкой кода, чтобы сбросить позицию потока тела обратно на 0.
request.Body.Position = 0;
Таким образом, любой код позже в жизненном цикле запроса найдет request.Body в таком состоянии, как если бы он еще не был прочитан.
Обновленный ответ
Извините, я неправильно понял ваш вопрос. По-прежнему применяется концепция обновления связанного потока до буферизованного потока. Однако вам нужно делать это вручную, мне неизвестны какие-либо встроенные функции .Net Core, которые позволяют вам читать поток ответов, однажды записанный таким образом, чтобы EnableRewind()
разработчик мог повторно прочитать поток запроса после того, как он был прочитан.
Ваш "хакерский" подход, вероятно, полностью уместен. Вы в основном конвертируете поток, который не может искать, в поток, который может. В конце дня Response.Body
поток должен быть заменен потоком, который буферизирован и поддерживает поиск. Вот еще один подход к промежуточному программному обеспечению, но вы заметите, что он очень похож на ваш подход. Однако я решил использовать блок finally в качестве дополнительной защиты для возврата исходного потока обратно в, Response.Body
и я использовал Position
свойство потока, а не Seek
метод, поскольку синтаксис немного проще, но эффект не отличается от вашего подхода.
public class ResponseRewindMiddleware
{
private readonly RequestDelegate next;
public ResponseRewindMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
Stream originalBody = context.Response.Body;
try {
using (var memStream = new MemoryStream()) {
context.Response.Body = memStream;
await next(context);
memStream.Position = 0;
string responseBody = new StreamReader(memStream).ReadToEnd();
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
} finally {
context.Response.Body = originalBody;
}
}
}
Эта статья взята из Интернета, укажите источник при перепечатке.
Если есть какие-либо нарушения, пожалуйста, свяжитесь с[email protected] Удалить.
я говорю два предложения