我有一个中间件,在那里我发现了错误,我想打印路由和正文中发送的信息,我使用了几个显示的代码,但返回的信息是空的。
这是我用来捕获源代码中可能出现的错误的中间件,我所做的是捕获错误并打印堆栈。
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/正文:|
你快到了:
var requestContent = "";
request.EnableBuffering();
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
requestContent = reader.ReadToEnd();
}
request.Body.Position = 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();
});
我想打印路由和body中发送的信息,我用 显示了几个代码,但返回的信息是空的。
由于 request.Body 是 stream 类 的类型因此,我们可以使用 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>();
输出: