简体   繁体   English

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

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

below is the code:下面是代码:

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

   }
}

I got only "write sth first" response, the content generated by controller's action method is not appended to the response.我只得到“先写”响应,控制器的动作方法生成的内容没有附加到响应中。 However, I put a debugger on the action method of the controller, the debugger was hitting, which means the MVC pipeline was still running(which is not really short circuit) then why the response from action method is not in the response?但是,我在控制器的 action 方法上放了一个调试器,调试器正在命中,这意味着 MVC 管道仍在运行(这并不是真正的短路)那么为什么来自 action 方法的响应不在响应中?

What's confusing me more is, if I change the code to:更让我困惑的是,如果我将代码更改为:

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

   }
}

then I got a response that is "write sth first\n "Hello World!", so why this time the response from the endpoint is appended to the Response?然后我得到一个响应是“先写东西\n“Hello World!”,那么为什么这次来自端点的响应被附加到响应中?

Is anywhere in endpoints.MapControllers() that check the HttpContext.Response like if there is content then abort the remaining process? endpoints.MapControllers()中是否有任何检查HttpContext.Response的地方,比如是否有内容然后中止剩余的进程? where is the source code for this part?这部分的源代码在哪里?

Here's the error when I tried这是我尝试时的错误

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

So it has nothing to do with middleware or MVC pipelines.所以它与中间件或 MVC 管道无关。 The controller needs to write HTTP headers but it cannot because the response has already started.控制器需要编写 HTTP 标头,但它不能,因为响应已经开始。

Seeing as other middleware might also need to write to the Response , you shouldn't start writing to it yourself.鉴于其他中间件可能也需要写入Response ,您不应该自己开始写入。 You could try attaching to the OnStarting event instead.您可以尝试附加到OnStarting事件。 That way you won't start the Response yourself (Pseudocode from memory):这样你就不会自己启动响应(内存中的伪代码):

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

    await next();
}

why this time the response from the endpoint is appended to the Response为什么这次来自端点的响应被附加到响应中

That is because the endpoint middleware is called after your custom middleware.那是因为端点中间件是在您的自定义中间件之后调用的。 So first it calls your middleware, then it calls the endpoint middleware.因此,它首先调用您的中间件,然后调用端点中间件。 Middleware order matters.中间件顺序很重要。

Based on tia's answer,I tried to replaced the default response body and the Response.HasStarted attribute is false,you could see the page was rendered with starttime and end time,context.Response.WriteAsync will not short circuit the pipeline :根据 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;
            });

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

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

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