如何在 NET 6 C#(WEB API)中从中间件读取请求主体

问题描述 投票:0回答:3

我有一个中间件,在那里我发现了错误,我想打印路由和正文中发送的信息,我使用了几个显示的代码,但返回的信息是空的。

这是我用来捕获源代码中可能出现的错误的中间件,我所做的是捕获错误并打印堆栈。

using System.Net.Mime;
using System.Net;
using System.Transactions;
using System.Text;
using Microsoft.AspNetCore.Http;

namespace MyWebApi.Middlewares
{
    public static class MyCustomMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyCustomMiddleware(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyCustomMiddleware>();
        }
    }

    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<MyCustomMiddleware> _logger;

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

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
                if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
                    throw new UnauthorizedAccessException();
            }
            catch (Exception ex)
            {
                _logger.LogError("({errorCode})Message Error: {ex}\r\nqueryString/Body:{queryString}", "500", ex, await GetInfo(context));
            }
        }

        private async Task<string> GetInfo(HttpContext context)
        {
            var request = context.Request;
            string result = string.Empty;
            if (request.Method == HttpMethods.Post && request.ContentLength > 0)
            {
                request.EnableBuffering();
                request.Body.Position = 0;
                var buffer = new byte[Convert.ToInt32(request.ContentLength)];
                await request.BodyReader.ReadAsync();
                //get body string here...
                var requestContent = Encoding.UTF8.GetString(buffer);

                request.Body.Position = 0;
                result = string.Concat('[', request.Method, "]: ", request.Path, '/', request.QueryString, "\r\nBody: ", requestContent);
            }
            else
            {
                result = string.Concat('[', request.Method, "]: ", request.Path, '/', request.QueryString);
            }

            return result;
        }
    }
}

程序.cs:

var app = builder.Build();

//Middlewares
app.UseMyCustomMiddleware();

在我的控制器中:

namespace MyWebApi.Controllers.V4
{
    [ApiController]
    [EnableCors("cors")]
    [Authorize]
    [Route("v{version:apiVersion}/Products")]
    [ApiVersion("4.0")]
    public class ProductsController : Controller
    {
        private readonly IConfiguration _configuration;
        private readonly IproductsBusinessLogic _productsBusinessLogic;
        private readonly IValidator<ProductsRequestDto> _ProductsRequestDtoValidator;
        private readonly ILogger<ProductsController> _logger;
        public ProductsController(
            ILogger<ProductsController> logger,
            IConfiguration configuration,
            IproductsBusinessLogic productsBusinessLogic,
            IValidator<ProductsRequestDto> ProductsRequestDtoValidator
            )
        {
            _logger = logger;
            _configuration = configuration;
            _productsBusinessLogic = productsBusinessLogic;
            _ProductsRequestDtoValidator = ProductsRequestDtoValidator;
        }

        [MapToApiVersion("4.0")]
        [HttpPost]
        public async Task<IActionResult> Register([FromBody] ProductsRequestDto request)
        {
            var results = await _ProductsRequestDtoValidator.ValidateAsync(request);
            results.AddToModelState(ModelState, null);
            if (!results.IsValid)
            {
                return new ValidationFailedResult(results);
            }
            var result = await _productsBusinessLogic.Register(request);
            return Ok(result);
        }
    }
}

在日志中打印出来:

queryString/正文:[POST]:/v4.0/Products/正文:|

c# asp.net-core .net-6.0 middleware httpcontext
3个回答
1
投票

你快到了:

var requestContent = "";
request.EnableBuffering();
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
    requestContent = reader.ReadToEnd();
}
request.Body.Position = 0;

0
投票

我在这篇文章中找到了解决方案:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-7.0#pipelines

我现在用的是BodyReader,最后我的代码变成了这样:

using System.Net.Mime;
using System.Net;
using System.Transactions;
using System.Text;
using Microsoft.AspNetCore.Http;

namespace MyWebApi.Middlewares
{
    public static class MyCustomMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyCustomMiddleware(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyCustomMiddleware>();
        }
    }

    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<MyCustomMiddleware> _logger;

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

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
                if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
                    throw new UnauthorizedAccessException();
            }
            catch (Exception ex)
            {
                _logger.LogError("({errorCode})Message Error: {ex}\r\nqueryString/Body:{queryString}", "500", ex, await GetInfo(context));
            }
        }

        private async Task<string> GetInfo(HttpContext context)
        {
            var request = context.Request;
            string strInfoBody = string.Empty;
            bool infoBody = request.ContentLength > 0;
            if (infoBody)
            {
                request.EnableBuffering();
                request.Body.Position = 0;
                List<string> tmp = await GetListOfStringFromPipe(request.BodyReader);
                request.Body.Position = 0;

                strInfoBody = string.Concat("\r\nBody: ", string.Join("", tmp.ToArray()));
            }

            return string.Concat('[', request.Method, "]: ", request.Path, '/', request.QueryString,(infoBody ? strInfoBody : string.Empty));
        }
        
        private async Task<List<string>> GetListOfStringFromPipe(PipeReader reader)
        {
            List<string> results = new List<string>();

            while (true)
            {
                ReadResult readResult = await reader.ReadAsync();
                var buffer = readResult.Buffer;

                SequencePosition? position = null;

                do
                {
                    // Look for a EOL in the buffer
                    position = buffer.PositionOf((byte)'\n');

                    if (position != null)
                    {
                        var readOnlySequence = buffer.Slice(0, position.Value);
                        AddStringToList(results, in readOnlySequence);

                        // Skip the line + the \n character (basically position)
                        buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
                    }
                }
                while (position != null);


                if (readResult.IsCompleted && buffer.Length > 0)
                {
                    AddStringToList(results, in buffer);
                }

                reader.AdvanceTo(buffer.Start, buffer.End);

                // At this point, buffer will be updated to point one byte after the last
                // \n character.
                if (readResult.IsCompleted)
                {
                    break;
                }
            }

            return results;
        }

        private static void AddStringToList(List<string> results, in ReadOnlySequence<byte> readOnlySequence)
        {
            // Separate method because Span/ReadOnlySpan cannot be used in async methods
            ReadOnlySpan<byte> span = readOnlySequence.IsSingleSegment ? readOnlySequence.First.Span : readOnlySequence.ToArray().AsSpan();
            results.Add(Encoding.UTF8.GetString(span));
        }
    }
}

在 Program.cs 中,在调用中间件的使用之前:

app.Use(async (context, next) => {
    context.Request.EnableBuffering();
    await next();
});

0
投票

我想打印路由和body中发送的信息,我用 显示了几个代码,但返回的信息是空的。

由于 request.Bodystream 类 的类型因此,我们可以使用 StreamReder 来提取请求主体,它也可以返回我们预期的结果。

让我们在实践中检验一下:

自定义请求阅读器型号:

public class RequstBodyReaderModel
        {
            public string? HttpVerb { get; set; }
            public string? RequestPath { get; set; }
            public string? RequestRawData { get; set; }
            public string? Message { get; set; }
        }

注意: 为了演示我使用上面的类来绑定我的请求主体的示例。

请求身体阅读器中间件:

public class ReadRequestBodyMiddleware
    {
        private readonly RequestDelegate _next;
        public ReadRequestBodyMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            var request = context.Request;
            var stream = request.Body;// At the begining it holding original request stream                    
            var originalReader = new StreamReader(stream);
            var originalContent = await originalReader.ReadToEndAsync(); // Reading first request

            //My Custom Response Class
            var readingRequestBody = new RequstBodyReaderModel();
            readingRequestBody.HttpVerb = request.Method;
            readingRequestBody.RequestPath = request.Path;
            readingRequestBody.RequestRawData = originalContent;
            readingRequestBody.Message = "Here I am Reading the request body";

            //converting my custom response class to jsontype
            var json = JsonConvert.SerializeObject(readingRequestBody);
            //Modifying existing stream
            var requestData = Encoding.UTF8.GetBytes(json);
            stream = new MemoryStream(requestData);
            request.Body = stream;
            await _next(context);
           
        }

        
    }

注意: 您可以保留现有的请求正文,为了测试我正在重新绑定 request.Body = stream; 中的修改值,但您可以根据需要自定义。

Program.cs中调用中间件:

app.UseMiddleware<ReadRequestBodyMiddleware>();

输出:

© www.soinside.com 2019 - 2024. All rights reserved.