中间件模式是经典的23中设计模式中的一种,也是.Net Core新引入的一种设计模式。我们在开发项目的过程中会大量的使用中间件。
在ASP.NET Core中,中间件是一个可以处理HTTP请求或响应的软件管道。在ASP.NET Core中,中间件具有非常特定的用途。比如,我们可能需要一个中间件验证用户,一个中间件处理错误,一个中间件来提供静态文件,如Javascript文件、CSS文件和图片等。
我们在ASP.NET Core中使用这些中间件设置请求处理管道,正是这管道决定了如何处理请求。而请求处理管道是由Startup.cs文件中的Configure()方法进行配置的,它也是应用程序启动的一个重要部分。
以下是Congifure()方法中的代码
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
="color: rgb(153, 153, 153);">16
如读者所见,由空项目模板生成的Configure()方法中,有一个非常简单的请求处理管道,其中有3个中间件。
现在为了更好地了解中间件,我们对代码进行修改,以下是修改后Configure()方法中的代码,有两个中间件。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World");
});
}
通过这个简单的请求处理管道,所有的应用程序都可以将消息写入,然后再由浏览器显示出来。
如图所示,显示中间以及它们如何适应请求处理管道。
在ASP.NET Core中,中间件可以同时处理传入请求和传出响应。因此,中间件可以处理传入请求并将该请求传递给管道中的下一个中间件以进行下一步处理。
如果我们有一个日志记录中间件,那么它可能只是记录请求的时间,处理完毕后将请求传递给下一个中间件以进行下一步处理。
终端中间件:该中间件可以处理请求,并决定不调用管道中的下一个中间件,从而使管道短路。微软将其命名为terminal middleware,翻译为终端中间件。短路通常是被允许的,因为它可以避免一些不必要的工作。
如果请求的是一个图片文件或一个CSS纯静态文件,那么StaticFiles中间件可以在处理该请求后,使管道中的其余部分短路。就是说,在图中,如果请求是针对静态文件,则StaticFiles中间件不会调用MVC中间件,从而避免一些无用的操作。
中间件可以通过传入的HTTP请求来响应HTTP请求。比如,管道中的MVC中间件负责处理对URL/students的请求并返回学生列表信息。
中间件还可以处理传出响应。比如,日志记录中间件可以记录响应发送时间。此外,它还可以通过计算接收请求时间和响应时间之间的差异来计算处理请求所花费的时间。
中间件是按照添加到管道的顺序执行的。我们要以正确的顺序添加中间件,否则应用程序可能无法按预期运行。有时候程序虽然会编译成功,但是依然无法正常使用。
在大型团队中,中间件应以NuGet包的形式提供。由NuGet处理更新,通过尽量将中间件拆的足够小,以提供每个中间件独立更新的能力。
在微服务架构中往往设计很多种中间件,大型互联网公司的架构团队会开发各种不同类型的中间件。
在我们的项目中,根据程序需求,可以向请求处理管道添加尽可能多的中间件。如果,我们要使用一些静态HTML页面和图像开发简单的Web应用程序,那么请求处理管道可能只包含StaticFiles中间件。这就是模块化设计的好处,让每个人都像玩积木一样。
另一方面,如果,我们需要开发一个安全的数据驱动设计的Web应用程序,那么可能需要几个中间,如StaticFiles中间件、身份验证中间件、授权中间件和MVC中间件等。
我们将使用中间件为ASP.NET Core应用程序配置请求处理管道。
作为应用程序启动的一部分,要在Configure()方法中设置请求处理管道。
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World");
});
}
}
="color: rgb(153, 153, 153);">16
目前的代码有两个中间件在管道中:UseDeveloperExceptionPage()方法和Run()方法。
UseDeveloperExceptionPage()中间件:顾名思义,如果存在异常并且环境变量是Development,此中间件会被调用,显示开发异常界面。我们将在以后讨论这个UseDeveloperExceptionPage()中间件和环境变量的配合使用。
第二个中间件是注册Run()方法到管道中,它只能处理信息传入的Response对象。目前,它是一个响应所有请求的中间件,返回“Hello World“。在这种情况下,无论请求路径是什么,所有请求都会被这个中间件处理,我们得到的返回值都是这个中间件调用Response对象返回的string类型的字符串。
请注意,返回的值是纯文本而不是HTML。我们可以通过检测页面源代码来确认这一点。可以看到,源代码中没有任何HTML标签,只是纯文本。
即使现在创建一个html文件,并且在请求中包含该文件的路径:http://localhost:5000/index.html,应用程序也无法返回该静态文件。这是因为目前我们的请求处理管道中没有可以提供静态文件的中间件,所以不支持HTML、图像、CSS和Javascript等文件的显示。在后面的内容中,我们将添加所需的中间件以便能够提供静态文件。
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World");
});
关于这段代码的解析如下:
app.Run(async (context) =>
{
context.Request.ContentType = "text/plain;charset=utf-8";
await context.Response.WriteAsync("从第一个中间件中打印Hello World");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("从第二个中间件中打印Hello World");
});
以上中间件的输出结果(在此未放结果截图)说明如下:
如果希望中间件能够调用管道中的下一个中间件,则应注册Use()中间件,如下所示:
app.Use(async (context, next) =>
{
await next();
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("从第二个中间件中打印Hello World");
});
注意,Use()方法有两个参数。第一个参数是HttpContext上下文对象;第二个参数是Func类型,即它是代表管道中下一个中间件的委托。
接下来看一看实践中间件的工作流程
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILogger<Startup> logger)
{
app.Use(async (context, next) =>
{
logger.LogInformation("MW1:传入请求");
await next();
logger.LogInformation("MW1:传出响应");
});
app.Use(async (context, next) =>
{
logger.LogInformation("MW2:传入请求");
await next();
logger.LogInformation("MW2:传出响应");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("MW3:处理请求并生成响应");
logger.LogInformation("MW3:处理请求并生成响应");
});
}
="color: rgb(153, 153, 153);">16
代码说明如下:
读者可以通过查看ASP.NET Core 3.1在Github的源代码来验证这一点。
.ConfigureLogging((hosting,logging)=>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
}).
代码说明如下:
我们使用依赖注入的方式将ILogger记录到系统中
如果使用.NET Core CLI运行项目,则可以在控制台窗口中查看记录的信息。
如果直接在Visual Studio上运行项目,则可以在输出窗口中查看记录的信息。
我们将看到,信息按以下顺序记录着
将输出结果与下图所示的内容结和起来理解
ASP.NET Core的中间件有以下特点:
中间件的工作流程如下:
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
加入交流群
请使用微信扫一扫!