Asp.net mvc 如何使用.NET核心中间件将HTML插入响应体
我一直在尝试拼凑一些中间件,使我能够测量请求的处理时间。这个例子给了我一个很好的起点,但我遇到了麻烦 在下面的代码中,我能够测量处理时间,并使用HTML Agility Pack将其插入到div中。但是,页面的原始内容会被复制。我想我对UpdateHtml中的context.Response.Body属性做了一些不正确的操作,但无法确定它是什么。我在代码中做了一些注释。如果你看到任何不正确的地方,请告诉我好吗 谢谢Asp.net mvc 如何使用.NET核心中间件将HTML插入响应体,asp.net-mvc,http,asp.net-core,html-agility-pack,asp.net-core-middleware,Asp.net Mvc,Http,Asp.net Core,Html Agility Pack,Asp.net Core Middleware,我一直在尝试拼凑一些中间件,使我能够测量请求的处理时间。这个例子给了我一个很好的起点,但我遇到了麻烦 在下面的代码中,我能够测量处理时间,并使用HTML Agility Pack将其插入到div中。但是,页面的原始内容会被复制。我想我对UpdateHtml中的context.Response.Body属性做了一些不正确的操作,但无法确定它是什么。我在代码中做了一些注释。如果你看到任何不正确的地方,请告诉我好吗 谢谢 public class ResponseMeasurementMiddlewa
public class ResponseMeasurementMiddleware
{
private readonly RequestDelegate _next;
public ResponseMeasurementMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var watch = new Stopwatch();
watch.Start();
context.Response.OnStarting(async () =>
{
var responseTime = watch.ElapsedMilliseconds;
var newContent = string.Empty;
var existingBody = context.Response.Body;
string updatedHtml = await UpdateHtml(responseTime, context);
await context.Response.WriteAsync(updatedHtml);
});
await _next.Invoke(context);
}
private async Task<string> UpdateHtml(long responseTime, HttpContext context)
{
var newContent = string.Empty;
var existingBody = context.Response.Body;
string updatedHtml = "";
//I think I'm doing something incorrectly in this using...
using (var newBody = new MemoryStream())
{
context.Response.Body = newBody;
await _next(context);
context.Response.Body = existingBody;
newBody.Position = 0;
newContent = await new StreamReader(newBody).ReadToEndAsync();
updatedHtml = CreateDataNode(newContent, responseTime);
}
return updatedHtml;
}
private string CreateDataNode(string originalHtml, long responseTime)
{
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(originalHtml);
HtmlNode testNode = HtmlNode.CreateNode($"<div><h2>Inserted using Html Agility Pack: Response Time: {responseTime.ToString()} ms.</h2><div>");
var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");
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;
}
}
对于重复的html,它是由wait_nextcontext引起的;在UpdateHtml中,它将调用其余的middlware,像MVC一样处理请求和响应 在等待下一个上下文的情况下;,您不应在context.Response.onstart中修改响应正文 对于解决方法,我建议您将ResponseMeasurementMiddleware作为第一个中间件,然后像下面这样计算时间
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
// 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><h2>Inserted using Html Agility Pack: Response Time: {responseTime.ToString()} ms.</h2><div>");
var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");
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;
}
}
对于这种方式,app.useMidleware;,该操作将是发送响应之前的最后一个操作,然后处理时间将适合于处理时间
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<ResponseMeasurementMiddleware>();
//rest middlwares
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}