中间件是ASP.NET Core的一个重要特点,ASP.NET Core应用程序之所以能够灵活地处理各种各样的请求,完成都是由于中间件,那么它究竟是怎么一回事呢?
一、理解中间件
ASP.NET Core的一个主要特点是中间件(Middleware),它通过中间件来处理传入的HTTP请求,并将处理结果返回给发起请求的客户端。多个中间件构成了中间件管道(Middleware pipeline),也称为请求管道。之所以称其为管道,是因为请求能够从一个中间件进入另一个中间件,并以相反的顺序再出来,如下图。
对于一个ASP.NET Core应用程序,所有向它发送的请求,都是通过它里面添加的中间件来进行处理并返回响应,比如显示网站的首页、对用户的认证、请求静态文件(CSS、JavaScript文件)等等。
另外,需要注意的是,传入的请求,不一定会遍历到所有添加的中间件,有可能在经过某一个中间件处理时,直接返回响应,而不再继续前进,比如,如果用户没有通过认证,或者程序出现异常时,就不会再继续被下一个中间件处理。
二、Program与Startup
在一个ASP.NET Core应用中,最主要的文件有两个,一个是Program.cs,另一个是Startup.cs,其中Program.cs里包含了程序的入口,即Program类中的Main函数。而Startup.cs文件中的Startup类则是用来配置当前应用程序。
Startup类包含了两个方法,即ConfigureServices和Configure,其中前者主要是用于向依赖注入容器添加服务,而后者则是用于为当前程序配置处理HTTP请求的请求管道;在这个方法里,我们可以根据需要添加多个中间件。
public class Startup
{public void ConfigureServices(IServiceCollection services){// 向依赖注入容器添加服务}public void Configure(IApplicationBuilder app, IHostingEnvironment env){// 配置中间件管道 }
}
三、添加中间件
添加中间件的方法有3种,这3种方法都是通过Configure方法的参数IApplicationBuilder接口所提供的方法实现的,这个接口提供Use、Map以及Run方法,均能够用来添加中间件。
1、Run方法最简单,它仅处理请求,并不会将请求传入下一个中间件。因此,对于如下代码,会直接返回包含一串字符的响应。
app.Run(async (context) =>{ await context.Response.WriteAsync("Hello World!");});
2、Use方法与Run方法不同的是,它在处理请求后,还可以把请求传递给下一个中间件,并由其继续处理。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{app.Use(async (context, next) =>{if (context.Request.Path.Value == "/welcome"){await context.Response.WriteAsync("Hello World!");}else{Debug.WriteLine("中间件 A 开始");// ....await next();// ...Debug.WriteLine("中间件 A 结束");}});app.Run(async (context) =>{await context.Response.WriteAsync("Hello World!");});
}
以上代码的运行结果是:显示“中间件 A 开始” -> 在响应正文中写入信息 -> 显示“中间件A结束。
注意:尽管Use方法能够让下一个中间件继续处理请求,它也可以直接处理,不再继续向前,在上例中如果访问/welcome将直接返回响应。
3、Map方法允许指定一个条件,如果请求符合该条件,则会新建一个分支,并在该分支上继承处理请求,而当请求处理结束后,不返回原来的分支。
public void Configure(IApplicationBuilder app)
{app.Use(async (context, next) =>{ Console.WriteLine("中间件 A:开始");await next();Console.WriteLine("中间件 A:结束");});app.Map(new PathString("/map"),a => a.Use(async (context, next) =>{Console.WriteLine("中间件 B:开始");await next();Console.WriteLine("中间件 B:结束");}));app.Run(async context =>{Console.WriteLine("中间件 C");await context.Response.WriteAsync("Hello world");});
}
当请求/map后,其结果如下:
中间件 A:开始
中间件 B:开始
中间件 B:结束
中间件 A:结束
可以看到,中间件C并未执行。
事实上,除了以上3个方法,ASP.NET Core还提供了UseWhen和MapWhen这两个方法,它们能够以更复杂的方式来添加中间件。
通过上面的介绍,我们可以明白两点,第一,中间件并不复杂,它本质上就是用于处理HTTP请求的一段代码;第二点,中间件的添加顺序很重要,因为它的添加顺序将决定应用程序如何处理HTTP请求。
四、自定义中间件
通过上面的方式添加的中间件,我们可以称为内联中间件(Inline Middleware),这种方式在实际开发中很少见,更常见的方式是创建自定义中间件类,也即,添加一个类,通过这个类来代表中间件并实现其功能。而这个类需要满足两个条件:
第一,它包含一个带有RequestDelegate类型参数的构造函数,这个RequestDelegate类型的参数表示在请求管道中的下一个中间件;
第二,它包含一个Invoke方法,该方法接受一个HttpContext类型的参数,并且这个方法的返回值为Task,这个HttpContext类型的参数即表示传入的HTTP请求。以下是一个典型自定义中间件:
public class CheckRequestPathMiddleware
{private RequestDelegate _next;public CheckRequestPathMiddleware(RequestDelegate requestDelegate){this._next = requestDelegate;}public Task Invoke(HttpContext context){if (context.Request.Path.Value == "/about"){context.Response.ContentType = "text/plain;charset=utf-8";context.Response.WriteAsync("Hello, 这条消息由自定义中间件输出");return Task.CompletedTask;}else{return _next(context);}}
}
当中间件创建好之后,要在Startup类中的Configure方法中把它添加到管道中,只要使用IApplicationBuilder接口的UseMiddleware方法。
app.UseMiddleware<CheckRequestPathMiddleware>();
ASP.NET Core提供了很多中间件,它们能够满足开发Web应用程序时所需要的各种功能,这些中间件包括:MVC、认证、静态文件、HTTPS重定向、CORS等。以下代码显示了一个标准应用程序所添加的中间件。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}else{app.UseExceptionHandler("/Home/Error");// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseCookiePolicy();app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});
}
以上这些以Use开头的方法均是向请求管道添加相应的中间件。我们可能注意到这些方法并不像我们刚才介绍的添加中间件的方法那样,即要么使用Use/Map/Run等,要么使用UseMiddleware方法。这里需要说明的是,以上这些以Use开头的方法本质上也是使用了UseMiddleware方法来添加中间件的。这些方法只不过是通过额外定义的一个扩展方法来更方便地添加相应的中间件而已。我们同样也可以创建类似的扩展方法,以方便别人使用我们所创建的自定义中间件。
public static class CheckRequestPathMiddlewareExtentions
{public static void UseCheckRequestPathMiddleware(this IApplicationBuilder app){app.UseMiddleware<CheckRequestPathMiddleware>();}
}
通过上面这个方法,我们就可以在Configure方法中直接调用app.UseCheckRequestPathMiddleware方法来添加这个自定义中间件了。
总结
本文简要地介绍了ASP.NET Core中所引入的中间件的概念以及其使用方法,中间件极大地方便了应用程序对HTTP请求的处理。多个中间件构成的管道为ASP.NET Core应用程序提供了更灵活的、更轻量的HTTP请求处理方式。