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));
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这段代码是一个 ASP.NET Core 中间件的实现,主要用于记录 HTTP 请求的参数,包括查询字符串和请求体。下面是对代码功能的详细解释:
类和构造函数
- HttpRequestRecord: 这是一个中间件类,负责处理 HTTP 请求并记录相关信息。
- 构造函数: 接受一个
RequestDelegate(用于调用下一个中间件)和一个IDiagnosticContext(用于记录诊断信息)。
InvokeAsync 方法
- InvokeAsync: 这是中间件的核心方法,接收一个
HttpContext对象。- 首先调用
EnrichFromRequestAsync方法来记录请求的查询字符串和请求体。 - 然后调用
next.Invoke(httpContext)继续处理请求。
- 首先调用
EnrichFromRequestAsync 方法
- EnrichFromRequestAsync: 这个方法用于从 HTTP 请求中提取信息并记录到
diagnosticContext。- 如果请求的查询字符串不为空,则将其记录到
diagnosticContext中。 - 调用
GetBodyContentAsync方法读取请求体的内容,并在内容不为空时记录到diagnosticContext。
- 如果请求的查询字符串不为空,则将其记录到
GetBodyContentAsync 方法
- GetBodyContentAsync: 这个方法用于异步读取 HTTP 请求体的内容。
- 使用
PipeReader读取请求体,直到读取到的内容长度超过 1024 字节(1 << 10)。 - 如果读取的内容长度超过最大限制,则只记录前 1024 字节,并在后面加上省略号(
...)。 - 读取完成后,将内容转换为字符串并返回。
- 使用
AppendString 方法
- AppendString: 这个方法用于将
ReadOnlySequence<byte>中的字节转换为字符串并追加到StringBuilder中。- 如果
ReadOnlySequence是单个段,则直接获取该段的字节;如果是多个段,则将其转换为数组并获取字节。
- 如果
总结
整体上,这个中间件的功能是:
- 在处理 HTTP 请求时,记录查询字符串和请求体的内容。
- 限制请求体的记录长度,避免记录过大的内容。
- 使用
IDiagnosticContext进行记录,方便后续的诊断和日志分析。
这个中间件可以用于调试和监控 HTTP 请求,帮助开发人员了解请求的详细信息。
评论加载中...