网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
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 &lt;&lt; 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 是单个段,则直接获取该段的字节;如果是多个段,则将其转换为数组并获取字节。

总结

整体上,这个中间件的功能是:

  1. 在处理 HTTP 请求时,记录查询字符串和请求体的内容。
  2. 限制请求体的记录长度,避免记录过大的内容。
  3. 使用 IDiagnosticContext 进行记录,方便后续的诊断和日志分析。

这个中间件可以用于调试和监控 HTTP 请求,帮助开发人员了解请求的详细信息。

loading