using System.Buffers;
using System.IO.Pipelines;
using Dpz.Core.Service.V4.Services;
using Wangkanai.Detection.Services;
namespace Dpz.Core.Web.Library.Middleware;
public class HttpRequestRecord(RequestDelegate next, IDiagnosticContext diagnosticContext)
{
public async Task InvokeAsync(
HttpContext httpContext,
IArticleService articleService,
IConfiguration configuration,
IDetectionService detectionService
)
{
if (httpContext.Request.Path.Equals("/sitemap.xml", StringComparison.OrdinalIgnoreCase))
{
var host = configuration.GetSection("upyun")["Host"] ?? "https://localhost:37701";
var baseUri = new Uri(host);
var uri = new Uri(baseUri, "/sitemap/sitemap.xml");
httpContext.Response.Redirect(uri.ToString());
return;
}
SetDetection(detectionService);
await EnrichFromRequestAsync(httpContext.Request);
await next.Invoke(httpContext);
var routeValues = httpContext.Request.RouteValues;
if (
routeValues["controller"] is "Article"
&& routeValues["action"] is "Read"
&& routeValues["id"] is string id
)
{
await articleService.ViewAsync(id);
}
}
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));
}
private void SetDetection(IDetectionService detectionService)
{
diagnosticContext.Set("Device", detectionService.Device.Type.ToString());
diagnosticContext.Set("Browser", detectionService.Browser.Name.ToString());
diagnosticContext.Set("BrowserVersion", detectionService.Browser.Version.ToString());
diagnosticContext.Set("Platform", detectionService.Platform.Name.ToString());
diagnosticContext.Set("Engine", detectionService.Engine.Name.ToString());
diagnosticContext.Set("Crawler", detectionService.Crawler.Name.ToString());
diagnosticContext.Set("UserAgent", detectionService.UserAgent.ToString());
}
}