【问题标题】:Append Custom HTML output before and after response in asp.net core?在asp.net核心响应之前和之后附加自定义HTML输出?
【发布时间】:2021-05-16 08:01:55
【问题描述】:

我有一个简单的 .net 核心应用程序,它发出 API 输出。

我的Configure 方法很简单:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env  )
        {

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
    }

这是 API 的当前输出:

仅出于测试目的,我想在响应之前和之后添加 HTML 标记:

类似(在 DOM 中手动编辑):

所以我添加了这个:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env  )
        {
            

          app.Use(async (context, next) =>
         {
             await context.Response.WriteAsync("<b> Hi</b>");
             await next ();
             await context.Response.WriteAsync("<b> Bye </b>");
         });




            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
    }

但是当我运行它时,我得到:

失败: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware1 执行请求时发生未处理的异常。 System.InvalidOperationException:标头是只读的,响应有 已经开始了。 使用此 HTML:

我一直在寻找 SO 中的解决方案,但没有找到,该怎么做。

问题:

为什么会这样?我以为我可以通过在管道上调用next() 来控制管道并做任何我想做的事情。

如何在前后添加自定义 HTML 标签?

编辑:

如果我将代码移动到 Configure 方法的末尾,我会看到 the regular output ,没有出现异常,但没有 HTML 标记。

编辑#2:

我也尝试过 OnStarting 事件,但仍然没有成功(我得到一个空白页面):

 app.Use(async (context, next) =>
        {
          
            context.Response.OnStarting(async state =>
            {
                if (state is HttpContext httpContext)
                {
                    var request = httpContext.Request;
                    var response = httpContext.Response;
                    await response .WriteAsync("<b> Bye </b>"); // <----
               }
            }, context);
            await next();
             
        });

【问题讨论】:

  • 你查看源代码了吗?你确定浏览器没有隐藏输出吗?
  • @bcg 是的。我做到了。它只写第一条语句。 i.imgur.com/RG0Ktwb.jpg(用于this代码
  • @OP 响应正文的内容类型是什么?
  • @PeterCsala 应用程序/json。 (我知道在之前和之后添加 HTML 标签是不匹配的。但我在纯文本污染之后。(我不介意在响应时将 appication/json 更改为 text/html ......)
  • @PeterCsala 你能看看this吗?

标签: c# asp.net-core .net-core


【解决方案1】:

使用以下中间件,我能够在操作结果之前和之后添加 html 标记:

public class BeforeAfterMiddleware
{
    private readonly RequestDelegate _next;
    public BeforeAfterMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        //Redirect response into a memorystream
        using var actionResponse = new MemoryStream();
        var httpResponse = context.Response.Body;
        context.Response.Body = actionResponse;

        //Call the handler action
        await _next.Invoke(context);

        //Read the handler response from the memory stream
        actionResponse.Seek(0, SeekOrigin.Begin);
        var reader = new StreamReader(actionResponse);
        using var bufferReader = new StreamReader(actionResponse);
        string body = await bufferReader.ReadToEndAsync();

        //Remove the handler's response from the memory stream
        context.Response.Clear();

        //Write data to the memorystream
        await context.Response.WriteAsync("<h1>HI</h1>");
        await context.Response.WriteAsync(body);
        await context.Response.WriteAsync("<h1>BYE</h1>");

        //Copy memorystream to the response stream
        context.Response.Body.Seek(0, SeekOrigin.Begin);
        await context.Response.Body.CopyToAsync(httpResponse);
        context.Request.Body = httpResponse;
    }
}
  1. 它只是将响应重定向到MemoryStream
  2. 然后用前后的一些文字改变它
  3. 最终将 memoryStream 重定向回响应流

用法:app.UseMiddleware&lt;BeforeAfterMiddleware&gt;();

【讨论】:

  • 做得很好...而且它是一个类而不是内联...
【解决方案2】:

好的,我想我知道了!这非常具有挑战性,因为您已经解决了...我的做法是编写自定义 IOutputFormatter

// in ConfigureServices()
services.AddControllers(opt =>
{
    opt.OutputFormatters.Clear();
    opt.OutputFormatters.Add(new AppendHtmlOutputFormatter());
});

// Formatter class
public class AppendHtmlOutputFormatter : IOutputFormatter
{
    public bool CanWriteResult(OutputFormatterCanWriteContext context) =>
        true; // add some logic here if you don't want to append all the time

    public Task WriteAsync(OutputFormatterWriteContext context)
    {
        var json = System.Text.Json.JsonSerializer.Serialize(context.Object);

        var modified = "<b>Hi!</b>" + json + "<b>Bye!</b>";
        return context.HttpContext.Response.WriteAsync(modified);
    }
}

现在,当我运行 API 端点时,我得到以下响应:

&lt;b&gt;Hi!&lt;/b&gt;{"Bar":42}&lt;b&gt;Bye!&lt;/b&gt;

这就是你要找的吗?

默认输出格式化程序

请注意,.Clear() 删除了以下默认 OutputFormatters - 按此顺序:

  1. HttpNoContentFormatter
  2. StringOutputFormatter
  3. StreamOutputFormatter
  4. SystemTextJsonOutputFormatter

上面的解决方案替换了所有这些并使用AppendHtmlOutputFormatter 处理所有内容。因此,以下可能是首选选项(尽管不会将 HTML 输出附加到所有内容):

// in ConfigureServices()
services.AddControllers(opt =>
{
    opt.OutputFormatters.Clear();
    opt.OutputFormatters.Add(new HttpNoContentOutputFormatter());
    opt.OutputFormatters.Add(new StreamOutputFormatter());
    opt.OutputFormatters.Add(new AppendHtmlOutputFormatter());
});

替代.Clear()

如果您不删除默认格式化程序,.NET 将使用它们并且永远不会访问自定义格式化程序。但是,如果您不想删除所有格式化程序(例如,另一个功能正在添加它们),您也可以按类型一次删除它们:

services.AddControllers(opt =>
{
    opt.OutputFormatters.RemoveType<StringOutputFormatter>();
    opt.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>();
    opt.OutputFormatters.Add(new AppendHtmlOutputFormatter());
});

【讨论】:

  • 是的。但我怎么知道故障格式化程序?顺便说一句,我已经设法以某种方式做到了....i.imgur.com/mcBANLP.jpg....I 确实在那里替换了,但同样,它是相同的(替换与添加)。这里要提到的一件事是 content.length 必须重新计算....
  • 我绝对建议尝试找到一个不涉及读取或修改响应正文的解决方案 - 因为 .NET Core 会尽其所能阻止您这样做。
  • 我添加了一个替代 Clear() 的方法,它允许删除可能更可取的特定 OutputFormatter。
  • 我想知道....为什么必须删除转换器?为什么我不能添加?
  • 如果您只是添加,您会发现没有任何变化,因为 .NET 在到达自定义之前使用了SystemTextJsonOutputFormatter。您可以在列表的前面插入自定义格式化程序,但删除它对我来说更有意义。
猜你喜欢
  • 1970-01-01
  • 2020-10-16
  • 2017-11-26
  • 2020-05-17
  • 1970-01-01
  • 2017-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多