将中间件的值注入 Razor 页面 HTML 正文

Inject value from middleware into Razor Page HTML body

我有一个计算 Razor 页面执行时间的中间件:

app.Use(async (context, next) =>
{
    var sw = new Stopwatch();
    sw.Start();
    await next.Invoke();
    sw.Stop();
    await context.Response.WriteAsync($"<div class=\"container\">Processing time: {sw.ElapsedMilliseconds} ms<div>");
});

它工作正常,但是 WriteAsync 创建的 HTML 被添加到响应的最后,在关闭 </html> 标签之后。

我如何放置 - 传递或注入 - 中间件计算的 sw.ElapsedMilliseconds 值在 HTML 体内的特定位置?使用 ASP.NET 核心 6.0.

也许你可以使用 HTML 敏捷包。

您可以使用LoadHtmlHtmlNode.CreateNode方法创建您要插入的html元素,然后使用DocumentNode.SelectSingleNode到select您要插入的位置。

测试可以参考以下代码:

自定义中间件:

//Install-Package HtmlAgilityPack
public class ResponseMeasurementMiddleware
{
        private readonly RequestDelegate _next;

        public ResponseMeasurementMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            var originalBody = context.Response.Body;
            var newBody = new MemoryStream();
            context.Response.Body = newBody;

            var watch = new Stopwatch();
            long responseTime = 0;
            watch.Start();
            await _next(context);
           // read the new body
            responseTime = watch.ElapsedMilliseconds;
            newBody.Position = 0;
            var newContent = await new StreamReader(newBody).ReadToEndAsync();
            // calculate the updated html
            var updatedHtml = CreateDataNode(newContent, responseTime);
            // set the body = updated html
            var updatedStream = GenerateStreamFromString(updatedHtml);
            await updatedStream.CopyToAsync(originalBody);
            context.Response.Body = originalBody;

        }
        public static Stream GenerateStreamFromString(string s)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }
        private string CreateDataNode(string originalHtml, long responseTime)
        {
           var htmlDoc = new HtmlDocument();
            htmlDoc.LoadHtml(originalHtml);
            HtmlNode testNode = HtmlNode.CreateNode($"<div class=\"container\">Processing time: {responseTime.ToString()} ms.</div>");
            var htmlBody = htmlDoc.DocumentNode.SelectSingleNode(".//body/div");
            if (htmlBody != null)
            {
                htmlBody.InsertBefore(testNode, htmlBody.FirstChild);
            }

            string rawHtml = htmlDoc.DocumentNode.OuterHtml; //using this results in a page that displays my inserted HTML correctly, but duplicates the original page content.
                                                             //rawHtml = "some text"; uncommenting this results in a page with the correct format: this text, followed by the original contents of the page

            return rawHtml;
        }
}

htmlDoc.DocumentNode.SelectSingleNode(".//body/div")htmlBody.InsertBefore(testNode, htmlBody.FirstChild)可以自定义插入的位置。

并使用自定义中间件:

app.UseMiddleware<ResponseMeasurementMiddleware>();

测试结果: