简体   繁体   English

asp.net核心如何在reuqest管道中运行“默认”RequestDelegate?

[英]how asp.net core run the "default" RequestDelegate in the reuqest pipeline?

I was reading the source code of ApplicationBuilder ( https://source.dot.net/#Microsoft.AspNetCore.Http/Builder/ApplicationBuilder.cs,132 ):我正在阅读ApplicationBuilder的源代码( https://source.dot.net/#Microsoft.AspNetCore.Http/Builder/ApplicationBuilder.cs,132 ):

public class ApplicationBuilder : IApplicationBuilder {

   private readonly List<Func<RequestDelegate, RequestDelegate>> _components = new();
   ...
   public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) {   
      _components.Add(middleware);
      return this;
   }

   public RequestDelegate Build() {
      RequestDelegate app = context => {  
         // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened
         // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
         var endpoint = context.GetEndpoint();
         var endpointRequestDelegate = endpoint?.RequestDelegate;
         if (endpointRequestDelegate != null) {
            var message = "...";
            throw new InvalidOperationException(message);
         }

         // endpoint is null 
         context.Response.StatusCode = StatusCodes.Status404NotFound;
         return Task.CompletedTask;
      }
   }

   for (var c = _components.Count - 1; c >= 0; c--) { 
      app = _components[c](app);
   }
   return app;
}

As you can see, the RequestDelegate app will always be executed, which means the status code will be set to 404, which doesn't make sense at all.如您所见,RequestDelegate app将始终执行,这意味着状态码将设置为 404,这完全没有意义。

let's we have the following code:让我们有以下代码:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
   ...
   app.UseRouting();

   app.UseEndpoints(endpoints => {
      endpoints.MapGet("routing", async context => {
         await context.Response.WriteAsync("Request Was Routed");
      });
   });

   app.Use(async (context, next) => { // let's call it terminal middleware
      await context.Response.WriteAsync("Terminal Middleware Reached");
   });
}

a default startup url without routing segement will reach to the terminal middleware the request has no assocaited endpoint and according the source code, the response's status code will be set to 404, which doesn't make sense at all.没有路由段的默认启动 url 将到达终端中间件,请求没有关联的端点,根据源代码,响应的状态码将设置为 404,这根本没有意义。 So my guess is, when UseEndpoints() is the last middleware as:所以我的猜测是,当UseEndpoints()是最后一个中间件时:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
   ...
   app.UseRouting();

   app.UseEndpoints(endpoints => {
      endpoints.MapGet("routing", async context => {
         await context.Response.WriteAsync("Request Was Routed");
      });
   });
}

The status code will be set to 404状态码将设置为 404

But when there are middlwares after UseEndpoints() like:但是当UseEndpoints()之后有中间件时,例如:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
   ...
   app.UseRouting();

   app.UseEndpoints(endpoints => {
      endpoints.MapGet("routing", async context => {
         await context.Response.WriteAsync("Request Was Routed");
      });
   });

   app.Use(async (context, next) => { 
      await context.Response.WriteAsync("Terminal Middleware Reached");
   });
}

asp.net core will check HttpContext 's reponse, if the response contain sth, then the the last app RequestDelegate in the source code won't run, is my understanding correct? asp.net 内核会检查HttpContext的响应,如果响应中包含 sth,那么源代码中的最后一个app RequestDelegate 将不会运行,我的理解是否正确?

Shortly yes, but there are some things where probably you should consider to care of, more in the comments below.很快是的,但有些事情可能你应该考虑关心,更多在下面的评论中。

public RequestDelegate Build() 
{
    RequestDelegate app = context => 
    {  
        var endpoint = context.GetEndpoint();
        var endpointRequestDelegate = endpoint?.RequestDelegate;

        // I was not able to produce this exception, or it is handled by the framework 
        // probably returns again 404 and do not executes all pending middlewares for sure.
        if (endpointRequestDelegate != null) 
        {
           var message = "...";
           throw new InvalidOperationException(message);
        }

        // however, hire is set 404 by default, case no endpoint found
        context.Response.StatusCode = StatusCodes.Status404NotFound;
        return Task.CompletedTask;
    }

    // Then each middleware in the pipeline will be executed solong there is nothing writen 
    // to the response body, at this point execution is broken and 
    // the response is returned to the client. 
    for (var c = _components.Count - 1; c >= 0; c--) 
    { 
        app = _components[c](app);
    }

    return app;
}

// To execute the code as you expect make sure to add all middlewares 
// in that order that Asp.Net expects

    static string msg = string.Empty;
    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.Use(async (context, next) =>
        {
            msg += "Middleware 1; ";

            await next();
        });

        app.Use(async (context, next) =>
        {
            msg += "Middleware 2; ";

            await next();
        });

        // use app.UseEndpoints... to register your endpoints, for exceptions and other staff
        // make the needed middlewares above and take care of their order
        app.UseEndpoints(x =>
        {
            x.Map("routing", async request =>
            {
                msg += "Routing Middleware Reached";
                await request.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(msg));
            });

            //x.Map("/", async request =>
            //{
            //    msg += "Terminal Middleware Reached";
            //    await request.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(msg));
            //});
        });

        // do not use like below, consider to put the middlewares before app.UseEndpoints...
        app.Use(async (context, next) =>
        {
            msg += "Middleware 3; ";

            await next();
        });

        app.Use(async (context, next) =>
        {
            msg += "Terminal Middleware Reached";
            await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(msg));
        });

        app.Use(async (context, next) =>
        {
            msg += "Middleware 4; ";

            await next();
        });
    }

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

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