Search code examples
c#asp.net-core.net-6.0middlewarehttpcontext

How to read request body from Middleware in NET 6 C# (WEB API)


I have a middleware, where I catch the errors and I want to print the route and the information sent in the body, I use several displayed codes but the information returned is empty.

this is the middleware I use to catch errors that may occur in my source code, what I do is catch the error and print the stack.

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;
        }
    }
}

Program.cs:

var app = builder.Build();

//Middlewares
app.UseMyCustomMiddleware();

in my controller:

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);
        }
    }
}

in the log is printed out:

queryString/Body:[POST]: /v4.0/Products/ Body: |


Solution

  • I found the solution in https://medium.com/@luisalexandre.rodrigues/logging-http-request-and-response-in-net-web-api-268135dcb27b

    Create below middleware and call it in program.cs/startup class.

    app.UseMiddleware<RequestResponseLoggerMiddlware>();

    private readonly RequestDelegate _next;
    private readonly ILogger<LoggingMiddleware> _logger;
    
    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }
    
    public async Task Invoke(HttpContext context)
    {
        await LogRequest(context);
    
        var originalResponseBody = context.Response.Body;
    
        using (var responseBody = new MemoryStream())
        {
            context.Response.Body = responseBody;
            await _next.Invoke(context);
            
            await LogResponse(context, responseBody, originalResponseBody);
        }
    }
    
    private async Task LogResponse(HttpContext context, MemoryStream responseBody, Stream originalResponseBody)
    {
        var responseContent = new StringBuilder();
        responseContent.AppendLine("=== Response Info ===");
        
        responseContent.AppendLine("-- headers");
        foreach (var (headerKey, headerValue) in context.Response.Headers)
        {
            responseContent.AppendLine($"header = {headerKey}    value = {headerValue}");
        }
    
        responseContent.AppendLine("-- body");
        responseBody.Position = 0;
        var content = await new StreamReader(responseBody).ReadToEndAsync();
        responseContent.AppendLine($"body = {content}");
        responseBody.Position = 0;
        await responseBody.CopyToAsync(originalResponseBody);
        context.Response.Body = originalResponseBody;
    
        _logger.LogInformation(responseContent.ToString());
    }
    
    private async Task LogRequest(HttpContext context)
    {
        var requestContent = new StringBuilder();
    
        requestContent.AppendLine("=== Request Info ===");
        requestContent.AppendLine($"method = {context.Request.Method.ToUpper()}");
        requestContent.AppendLine($"path = {context.Request.Path}");
    
        requestContent.AppendLine("-- headers");
        foreach (var (headerKey, headerValue) in context.Request.Headers)
        {
            requestContent.AppendLine($"header = {headerKey}    value = {headerValue}");
        }
    
        requestContent.AppendLine("-- body");
        context.Request.EnableBuffering();
        var requestReader = new StreamReader(context.Request.Body);
        var content = await requestReader.ReadToEndAsync();
        requestContent.AppendLine($"body = {content}");
    
        _logger.LogInformation(requestContent.ToString());
        context.Request.Body.Position = 0;
    }
    

    }