来源https://docs.asp.net/en/latest/fundamentals/middleware.htmlhtml
一些能够整合进http请求管道的小的应用组件称作中间件。ASP.NET 5集成支持中间件,能够经过应用的Configure方法来配置中间件。web
中间件是一些能够装配在应用请求管道中的一些组件,能够用来处理请求和响应。每一个中间件能够以为是否将请求转给管道中的下一个组件,也能够在下一个组件前或后执行一些操做。Request delegates用来构建请求管道,而后处理每个到应用的请求。json
Request delegates是经过IApplicationBuilder类型的Run,Map,Use扩展方法来配置,IApplicationBuilder是在Startup类中传给Configure方法的。request delegate能够是内嵌的匿名方法,也能够是定义在一个可服用的类中。这写可服用的类就是中间件,管道中的每一个中间件都负责invoke管道中的下一个中间件,或者在适当的状况下之间短路管道中的request delegate链(直接返回)。服务器
ASP.NET请求管道由一系列的请求代理(request delegate)组成,一个接一个的被调用。以下图所示,黑色箭头标识执行线程
session
每一个代理都在能够在下一个代理以前和以后执行一些操做。任何一个代理能够选择是将请求转给下一个代理仍是本身进行处理。直接处理就是短路执行管道,这样能够在某些状况下避免执行无谓的操做。好比受权组件,能够在受权经过的状况下将请求转个下一个组件,或者在未受权的状况下直接返回未受权。还有异常处理组件,要在管道的较前方来调用,这样才能捕获后面组件链上抛出的异常。
在默认的web模板中,你能够看到用请求代理来配置管道的例子,以下Configure方法,首先wire up 错误页面(in development)或者网站的生产环境的error handler,而后构建了支持static files, ASP.NET Identity authentication, and finally, ASP.NET MVC的管道。app
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear()); app.UseStaticFiles(); app.UseIdentity(); // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715 app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
由于管道是按顺序构建的,在生产环境下UseExceptionHandler配置在了管道的较前方,因此可以捕获管道上稍后被调用的组件抛出的异常。并且在这里,UseStaticFiles是在UseIdentity以前配置的,这样静态文件就不须要受权认证,这样能够提升性能(ASP.NET 5中静态文件都在wwwroot,默认是均可以访问的,是不须要执行受权组件的 )。对与非静态文件的请求则能够流转到管道中的下一个组件。 Learn more about Working with Static Files.asp.net
Note: 在Configure方法中配置的中间件Use[Middleware],顺序很重要。async
最简单的ASP.NET应用就是设置一个请求代理来处理全部请求。在这种状况下没有“真正的”请求管道,由于只有一个匿名函数来处理全部请求。ide
app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); });
这里很重要的一点是,如上所写的请求代理会终结管道,无论你是否有别的App.Run调用。在下面的例子中,只有第一个代理会被调用。函数
public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); }); app.Run(async context => { await context.Response.WriteAsync("Hello, World, Again!"); });
能够经过next调用来将多个request代理连接起来,这里next参数表明管道中的下一个请求代理。注意,虽然你调用了next,但并不意味你不能在下个请求代理前或者后来执行操做,以下例所示。
public void ConfigureLogInline(IApplicationBuilder app, ILoggerFactory loggerfactory) { loggerfactory.AddConsole(minLevel: LogLevel.Information); var logger = loggerfactory.CreateLogger(_environment); app.Use(async (context, next) => { logger.LogInformation("Handling request."); await next.Invoke(); logger.LogInformation("Finished handling request."); }); app.Run(async context => { await context.Response.WriteAsync("Hello from " + _environment); }); }
警告: Be wary of modifying HttpResponse after invoking next, since one of the components further down the pipeline may have written to the response, causing it to be sent to the client.(在invoke next以后尽可能不要修改HttpResponse,由于管道后面的组件可能已经写入了response)
Note: 环境设置为LogInline,ConfigureLogInline方法会在应用运行时调用。更多见Working with Multiple Environments.在剩下的文章中,咱们可以使用Configure[Environment]来显示不一样的选项。最简单的运行样例的方式是使用web命令,它是在project.json中配置的。
在上面的例子中,await next.Invoke() 会使程序进入到第14行,客户端收到预期的响应“Hello from LogInline”,而后服务器控制台输出组件前和组件后的消息,以下所示。
。
能够经过扩展方法Run, Map, and Use来配置管道。按照约定,Run是一种简便的添加中间件到管道的方法,而且不会调用其余剩余中间件。因此Run应该在管道的最后被调用。Run是一种约定,一些中间件暴露出它的Run[Middleware]方法,这些方法应该只在管道的末端调用。以下例子(one using Run and the other Use)是等价的,由于第二个没有调用next方法。
public void ConfigureEnvironmentOne(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello from " + _environment); }); } public void ConfigureEnvironmentTwo(IApplicationBuilder app) { app.Use(next => async context => { await context.Response.WriteAsync("Hello from " + _environment); }); }
Note: IApplicationBuilder接口自己暴露出了Use方法,因此技术上他们不全是扩展方法。
关于Use的例子,咱们已经看了好几个。**Map*扩展方法是一种branching the pipeline的约定。当前的实现支持基于请求路径的分支和使用断言的(predicate)**。Map扩展方法能够用来基于请求路径来匹配请求代理。Map 仅仅接受路径和配置单独中间件的管道的函数。以下例所示,根路径为/base path的请求,都会由HandleMapTest方法中配置的管道处理。
private static void HandleMapTest(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Map Test Successful"); }); } public void ConfigureMapping(IApplicationBuilder app) { app.Map("/maptest", HandleMapTest); }
Note: 当使用Map时,对每一个请求,匹配的path segment会从HttpResponse.Path中移除,并添加到HttpRequest.PathBase.
除了基于路径的匹配,MapWhen支持基于断言的管道分支,运行灵活的构建一个seperate管道。任何知足Func<HttpContext, bool>的断言,均可以使用,将请求map到一个新的管道。在下例中,一个简单的断言来检查请求参数branch
private static void HandleBranch(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Branch used."); }); } public void ConfigureMapWhen(IApplicationBuilder app) { app.MapWhen(context => { return context.Request.Query.ContainsKey("branch"); }, HandleBranch); app.Run(async context => { await context.Response.WriteAsync("Hello from " + _environment); }); }
在此例中任何知足断言的请求都会由定义在HandleBranch中的管道来处理。其余的请求在会有17行的定义的代理来处理。
ASP.NET 内有以下自带中间件
Middleware | Description |
---|---|
Authentication | Provides authentication support. |
CORS | Configures Cross-Origin Resource Sharing. |
Diagnostics | Includes support for error pages and runtime information. |
Routing | Define and constrain request routes. |
Session | Provides support for managing user sessions. |
Static Files | Provides support for serving static files, and directory browsing. |
对于较附加的请求处理函数,建议使用单独的类来构建中间件,经过暴露出能够在Configure中调用的IApplicationBuilder的扩展方法。把以前简单的logging 中间件转换到单独的一个类中,并把RequestDelegate 做为构造器参数,支持Invoke方法,以下。
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.Framework.Logging; using System.Threading.Tasks; namespace MiddlewareSample { public class RequestLoggerMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public RequestLoggerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) { _next = next; _logger = loggerFactory.CreateLogger<RequestLoggerMiddleware>(); } public async Task Invoke(HttpContext context) { _logger.LogInformation("Handling request: " + context.Request.Path); await _next.Invoke(context); _logger.LogInformation("Finished handling request."); } } }
中间件遵循 Explicit Dependencies Principle。并经过构造器暴露出全部依赖。中间件利用 UseMiddleware
public static class RequestLoggerExtensions { public static IApplicationBuilder UseRequestLogger(this IApplicationBuilder builder) { return builder.UseMiddleware<RequestLoggerMiddleware>(); } }
使用扩展方法和相关连的中间件类,使得Configure方法具备良好的可读性。
public void ConfigureLogMiddleware(IApplicationBuilder app, ILoggerFactory loggerfactory) { loggerfactory.AddConsole(minLevel: LogLevel.Information); app.UseRequestLogger(); app.Run(async context => { await context.Response.WriteAsync("Hello from " + _environment); }); }
尽管RequestLoggerMiddleware须要构造器参数ILoggerFactory,Startup类和UseRequestLogger 扩展方法都没显式提供。可是经过UseMiddleware
Testing the middleware (by setting the ASPNET_ENV environment variable to LogMiddleware) should result in output like the following (when using WebListener):
Note:另外一个是用UseMiddleware
Middleware provide simple components for adding features to individual web requests. Applications configure their request pipelines in accordance with the features they need to support, and thus have fine-grained control over the functionality each request uses. Developers can easily create their own middleware to provide additional functionality to ASP.NET applications.