Как вставить HTML в тело ответа с помощью промежуточного программного обеспечения .NET Core - PullRequest
0 голосов
/ 14 февраля 2019

Я пытался собрать некоторое промежуточное программное обеспечение, которое позволит мне измерить время обработки запроса. Этот пример дал мне хорошую отправную точку, но у меня возникли проблемы.

В приведенном ниже коде я могу измерить время процесса и вставить его в блок (используяHTML Agility Pack).Однако исходное содержимое страницы дублируется.Я думаю, что я делаю что-то неправильно со свойством context.Response.Body в UpdateHtml(), но не могу понять, что это такое.(Я сделал несколько комментариев в коде.) Если вы видите что-то, что выглядит неправильно, пожалуйста, дайте мне знать?

Спасибо.

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;
    }
}

1 Ответ

0 голосов
/ 15 февраля 2019

Для дублированного html это вызвано await _next(context); в UpdateHtml, который вызовет промежуточное программное обеспечение, такое как MVC, для обработки запросов и ответов.

Без await _next(context); не следует изменять тело ответа в context.Response.OnStarting.

В качестве обходного пути я бы предложил вам установить 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;
    }
}

, и зарегистрировать ResponseMeasurementMiddleware, например

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?}");
    });
}

Для этого пути app.UseMiddleware<ResponseMeasurementMiddleware>(); действие будет последним действием перед отправкой ответа, и тогда время обработки будет подходящим для времени обработки.

...