网站首页 网站源码
using System.Buffers;
using System.IO.Pipelines;
namespace Dpz.Core.WebApi.Middleware;
/// <summary>
/// 记录 HTTP 请求参数
/// </summary>
/// <param name="next"></param>
/// <param name="diagnosticContext"></param>
public class HttpRequestRecord(RequestDelegate next, IDiagnosticContext diagnosticContext)
{
/// <summary>
///
/// </summary>
/// <param name="httpContext"></param>
public async Task InvokeAsync(HttpContext httpContext)
{
await EnrichFromRequestAsync(httpContext.Request);
await next.Invoke(httpContext);
}
private async Task EnrichFromRequestAsync(HttpRequest request)
{
if (request.Query is { Count: > 0 })
{
diagnosticContext.Set("QueryString", request.Query);
}
var bodyContent = await GetBodyContentAsync(request.BodyReader);
if (bodyContent != string.Empty)
{
diagnosticContext.Set("RequestBody", bodyContent);
}
}
/// <summary>
/// 读取 http body
/// 如果 body 超过 1 << 10 字节,则不记录 body
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
private static async Task<string> GetBodyContentAsync(PipeReader reader)
{
var result = new StringBuilder();
const long maxLength = 1 << 10;
long totalLength = 0;
while (true)
{
var readResult = await reader.ReadAsync();
var buffer = readResult.Buffer;
// 如果 buffer 长度已经大于等于 maxLength,直接截取并返回
if (buffer.Length >= maxLength)
{
var span = buffer.Slice(0, maxLength);
AppendString(result, span);
result.Append("...");
reader.AdvanceTo(buffer.Start, buffer.GetPosition(maxLength));
//await reader.CompleteAsync();
break;
}
totalLength += buffer.Length;
if (readResult.IsCompleted && buffer.Length > 0)
{
AppendString(result, in buffer);
}
reader.AdvanceTo(buffer.Start, buffer.End);
if (totalLength >= maxLength)
{
result.Append("...");
break;
}
if (readResult.IsCompleted)
{
break;
}
}
return result.ToString();
}
private static void AppendString(
StringBuilder result,
in ReadOnlySequence<byte> readOnlySequence
)
{
var span = readOnlySequence.IsSingleSegment
? readOnlySequence.First.Span
: readOnlySequence.ToArray().AsSpan();
result.Append(Encoding.UTF8.GetString(span));
}
}
这段代码是一个 ASP.NET Core 中间件的实现,主要用于记录 HTTP 请求的参数,包括查询字符串和请求体。下面是对代码功能的详细解释:
RequestDelegate
(用于调用下一个中间件)和一个 IDiagnosticContext
(用于记录诊断信息)。HttpContext
对象。EnrichFromRequestAsync
方法来记录请求的查询字符串和请求体。next.Invoke(httpContext)
继续处理请求。diagnosticContext
。diagnosticContext
中。GetBodyContentAsync
方法读取请求体的内容,并在内容不为空时记录到 diagnosticContext
。PipeReader
读取请求体,直到读取到的内容长度超过 1024 字节(1 << 10)。...
)。ReadOnlySequence<byte>
中的字节转换为字符串并追加到 StringBuilder
中。ReadOnlySequence
是单个段,则直接获取该段的字节;如果是多个段,则将其转换为数组并获取字节。整体上,这个中间件的功能是:
IDiagnosticContext
进行记录,方便后续的诊断和日志分析。这个中间件可以用于调试和监控 HTTP 请求,帮助开发人员了解请求的详细信息。