Как прочитать тело запроса несколько раз в промежуточном программном обеспечении asp net core 2.2?

Я попробовал это: Прочитать тело запроса дважды, и это: https://github.com/aspnet/Mvc/issues/4962 но не сработало. Я читаю тело запроса так:

app.Use(async (context, next) =>
{
    var requestBody = await ReadStream(context.Request.Body);
    var requestPath = context.Request.Path.ToString();
    //Do some thing

    await next.Invoke();

    var responseStatusCode = context.Response.StatusCode;
    //Do some other thing
});

private async Task<string> ReadStream(Stream stream)
{
    using (var streamReader = new StreamReader(stream))
    {
        var result = await streamReader.ReadToEndAsync();

        return result;
    }
}

В контроллере я получаю "удаленный объект" или "пустой поток".

2 ответа

.netcore 3.1 версия ответа @HoussamNasser выше. Я создал многоразовую функцию для чтения тела запроса. Обратите внимание на изменение:HttpRequestRewindExtensions.EnableBuffering(request). EnableBuffering теперь является частью класса HttpRequestRewindExtensions.

public async Task<JObject> GetRequestBodyAsync(HttpRequest request)
    {
        JObject objRequestBody = new JObject();

        // IMPORTANT: Ensure the requestBody can be read multiple times.
        HttpRequestRewindExtensions.EnableBuffering(request);

        // IMPORTANT: Leave the body open so the next middleware can read it.
        using (StreamReader reader = new StreamReader(
            request.Body,
            Encoding.UTF8,
            detectEncodingFromByteOrderMarks: false,
            leaveOpen: true))
        {
            string strRequestBody = await reader.ReadToEndAsync();
            objRequestBody = SerializerExtensions.Deserialize<JObject>(strRequestBody);

            // IMPORTANT: Reset the request body stream position so the next middleware can read it
            request.Body.Position = 0;
        }

        return objRequestBody;
    }

Эта функция вернет JObject, который может использоваться для чтения свойств объекта Request Body. SerializerExtensions - это мое собственное расширение для сериализации и десериализации.

В промежуточном программном обеспечении вы можете ввести IHttpContextAccessor httpContextAccessorв конструкторе. А затем получите доступ к объекту запроса, напримерHttpRequest request = _httpContextAccessor.HttpContext.Request;. Наконец, можно вызвать многоразовую функцию, напримерGetRequestBodyAsync(request)

После еще нескольких попыток и использования "context.Request.EnableRewind()" это наконец-то работает так:

app.Use(async (context, next) =>
{
    context.Request.EnableRewind();
    var stream = context.Request.Body;

    using (var reader = new StreamReader(stream))
    {
        var requestBodyAsString = await reader.ReadToEndAsync();

        if (stream.CanSeek)
            stream.Seek(0, SeekOrigin.Begin);

        //Do some thing

        await next.Invoke();

        var responseStatusCode = context.Response.StatusCode;
        //Do some other thing
    }
});

Когда поток читается второй раз, указатель потока устанавливается в последнюю позицию. Вы должны попытаться переместить его обратно в нулевое положение, чтобы прочитать его снова с самого начала.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;

namespace Test_Middlewares.Middlewares
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class LoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<LoggingMiddleware> _logger;

        public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext httpContext)
        {
            var HttpContextBody = httpContext.Request.Body;
            string requestBody = "";

            httpContext.Request.EnableBuffering();

            // Leave the body open so the next middleware can read it.
            using (var reader = new StreamReader(
                httpContext.Request.Body,
                encoding: Encoding.UTF8,
                detectEncodingFromByteOrderMarks: false,
                bufferSize: -1,
                leaveOpen: true))
            {
                var body = await reader.ReadToEndAsync();
                // Do some processing with body…

                // Reset the request body stream position so the next middleware can read it
                httpContext.Request.Body.Position = 0;
            }

            _logger.LogDebug("Middleware 1 body =" + requestBody);

            await _next.Invoke(httpContext);
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class LoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<LoggingMiddleware>();
        }
    }
}

Для получения дополнительной информации перейдите по этим ссылкам:

https://devblogs.microsoft.com/aspnet/re-reading-asp-net-core-request-bodies-with-enablebuffering/

https://gunnarpeipman.com/aspnet-core-request-body/

Я боролся с этим, .net core 3.1 имеет критические изменения, "context.Request.EnableRewind();" в.net core 3.1 БОЛЬШЕ НЕТ, больше полезных советов можно найти здесь

Рабочий код в asp.net mvc api .net core 3.1 (startup.cs)

            app.Use(async (context, next) =>
        {
            context.Request.EnableBuffering();
            var stream = context.Request.Body;

            using (var reader = new StreamReader(stream))
            {
                var requestBodyAsString = await reader.ReadToEndAsync();

                if (stream.CanSeek)
                    stream.Seek(0, SeekOrigin.Begin);
               
                //some logging and other stuff goes here

                await next.Invoke();

            }
        });

Это, вероятно, из-за using заявление вокруг вашего StreamReader, Использование располагает StreamReader который вызывает распоряжение на базовом потоке. Смотрите ответ здесь для более подробной информации. Вы можете попробовать сохранить ссылку и избавиться от StreamReader после await next.Invoke(); завершается.

Другие вопросы по тегам