繁体   English   中英

为什么要写入 HttpContext.Response “短路” MVC 管道

[英]why writing to HttpContext.Response "short-circuit" MVC pipeline

下面是代码:

public class Startup
{
   // ...
   public void Configure(IApplicationBuilder app)
   {
      app.UseRouting();

      app.Use(async (context, next) =>
      {
         await context.Response.WriteAsync("write sth first\n");
         await next();
      });

      app.UseEndpoints(endpoints =>
      {
         endpoints.MapControllers();
      });

   }
}

我只得到“先写”响应,控制器的动作方法生成的内容没有附加到响应中。 但是,我在控制器的 action 方法上放了一个调试器,调试器正在命中,这意味着 MVC 管道仍在运行(这并不是真正的短路)那么为什么来自 action 方法的响应不在响应中?

更让我困惑的是,如果我将代码更改为:

public class Startup
{
   // ...
   public void Configure(IApplicationBuilder app)
   {
      app.UseRouting();

      app.Use(async (context, next) =>
      {
         await context.Response.WriteAsync("write sth first\n");
         await next();
      });

      app.UseEndpoints(endpoints =>
      {
         endpoints.MapGet("/", async context => {   
            await context.Response.WriteAsync("Hello World!");
         });
      });

   }
}

然后我得到一个响应是“先写东西\n“Hello World!”,那么为什么这次来自端点的响应被附加到响应中?

endpoints.MapControllers()中是否有任何检查HttpContext.Response的地方,比如是否有内容然后中止剩余的进程? 这部分的源代码在哪里?

这是我尝试时的错误

System.InvalidOperationException: Headers are read-only, response has already started.

所以它与中间件或 MVC 管道无关。 控制器需要编写 HTTP 标头,但它不能,因为响应已经开始。

鉴于其他中间件可能也需要写入Response ,您不应该自己开始写入。 您可以尝试附加到OnStarting事件。 这样你就不会自己启动响应(内存中的伪代码):

app.Use(async (context, next) =>
{
    context.Response.OnStarting(async () =>
    {
        await context.Response.WriteAsync("write sth first\n");
    });

    await next();
}

为什么这次来自端点的响应被附加到响应中

那是因为端点中间件是在您的自定义中间件之后调用的。 因此,它首先调用您的中间件,然后调用端点中间件。 中间件顺序很重要。

根据 tia 的回答,我尝试替换默认的响应正文,并且 Response.HasStarted 属性为 false,您可以看到页面以开始时间和结束时间呈现,context.Response.WriteAsync 不会使管道短路:

app.Use(async (context, next) =>
            {
                var response = context.Response;
                var responseOriginalBody = response.Body;
                using var memStream = new MemoryStream();
                response.Body = memStream;

                await context.Response.WriteAsync(string.Format("<a>starttime:{0}</a>", DateTime.Now.ToString("yyyy/MM/dd/HH:mm:ss.fff")));
                var start = context.Response.HasStarted;                
                await next.Invoke();
                await context.Response.WriteAsync(string.Format("<a>endtime:{0}</a>", DateTime.UtcNow.ToString("yyyy/MM/dd/HH:mm:ss.fff")));
                
                memStream.Position = 0;
                await memStream.CopyToAsync(responseOriginalBody);
                response.Body = responseOriginalBody;
            });

结果: 在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM