在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
在第1章项目结构分析中,我们提到 新旧版本之间的Pipeline区别 ASP.NET 5和之前版本的最大区别是对HTTP Pipeline的全新重写,在之前的版本中,请求过滤器的通常是以 HttpModule代码不仅可以在Global.asax中的各事件周期中进行添加,还可以单独编译成类库并在web.config中进行注册。 新版的ASP.NET 5抛弃了重量级的System.Web.dll,相应地引入了 Pass through components that form a pipeline between a server and application to inspect, route, or modify request and response messages for a specific purpose. 该定义和传统的 Middleware的注册和配置 在ASP.NET5中,request请求管线(Pipeline)的访问是在Startup类中进行的,该类时一个约定类,并且里面的 Middleware中的依赖处理:ConfigureServices方法 在ASP.NET5中的各种默认的Middleware中,都使用了依赖注入的功能,所以在使用Middleware中的功能时,需要提前将依赖注入所需要的类型及映射关系都注册到依赖注入管理系统中,即IServiceCollection集合,而ConfigureServices方法接收的就一个IServiceCollection类型的参数,该参数就是所有注册过类型的集合,通过原生的依赖注入组件进行管理(关于ASP.NET5中的依赖注入,我们会在单独章节中进行讲解),在该方法内,我们可以向该集合中添加新的类型和类型映射关系,示例如下: // Add MVC services to the services container. services.AddMvc(); 示例中的代码用于向系统添加Mvc模块相关的Service类型以支撑MVC功能,该方法是一个扩展方法,用于在集合中添加与MVC相关的多个类型。 Middleware的注册和配置:Configure方法 Configure方法的签名如下: public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) { // ... }
// Add static files to the request pipeline. app.UseStaticFiles(); // Add cookie-based authentication to the request pipeline. app.UseIdentity(); // Add MVC to the request pipeline. app.UseMvc(routes =>{ /*...*/}); 示例中的 public interface IApplicationBuilder { //... IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); } 通过代码,可以看出,middleware是 public delegate Task RequestDelegate(HttpContext context); 通过源码,我们可以看出, 注意: Middleware的定义 既然每个middleare都是 构造函数的第一个参数必须是处理管线中的下一个处理函数,即RequestDelegate;必须有一个 Invoke 函数, 并接受上下文参数(即HttpContent), 然后返回 Task; 示例如下: public class MiddlewareName { RequestDelegate _next; public MiddlewareName(RequestDelegate next) { _next = next;// 接收传入的RequestDelegate实例 } public async Task Invoke(HttpContext context) { // 处理代码,如处理context.Request中的内容 Console.WriteLine("Middleware开始处理"); await _next(context); Console.WriteLine("Middleware结束处理"); // 处理代码,如处理context.Response中的内容 } } 通过该模板代码可以看到,首先一个Middleware的构造函数要接收一个RequestDelegate的实例,先保存在一个私有变量里,然后通过调用 举个例子,如果我们要想记录页面的执行时间,首先,我们先定义一个TimeRecorderMiddleware,代码如下: public class TimeRecorderMiddleware { RequestDelegate _next; public TimeRecorderMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { var sw = new Stopwatch(); sw.Start(); await _next(context); var newDiv = @"<div id=""process"">页面处理时间:{0} 毫秒</div></body>"; var text = string.Format(newDiv, sw.ElapsedMilliseconds); await context.Response.WriteAsync(text); } } Middleware的注册有很多种方式,如下是实例型注册代码: app.Use(next => new TimeRecorderMiddleware(next).Invoke); 或者,你也可以使用UseMiddleware扩展方法进行注册,示例如下: app.UseMiddleware<TimeRecorderMiddleware>(); //app.UseMiddleware(typeof(TimeRecorderMiddleware)); 两种方式都可以 当然,你也可以定义一个自己的扩展方法用于注册该Middleware,代码如下: public static IApplicationBuilder UseTimeRecorderMiddleware(this IApplicationBuilder app) { return app.UseMiddleware<TimeRecorderMiddleware>(); } 最后在Startup类的Configure方法内进行注册,代码如下: public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) { app.UseTimeRecorderMiddleware(); // 要放在前面,以便进行统计,如果放在Mvc后面的话,就统计不了时间了。 // 等等 } 编译,重启,并访问页面,在页面的底部即可看到页面的运行时间提示内容。 常用Middleware功能的使用 app.UseErrorPage() app.UseErrorHandler("/Home/Error") app.UseStaticFiles() app.UseIdentity() 直接使用委托定义Middleware的功能 由于Middleware是 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) { app.Use(new Func<RequestDelegate, RequestDelegate>(next => content => Invoke(next, content))); // 其它 } // 注意Invoke方法的参数 private async Task Invoke(RequestDelegate next, HttpContext content) { Console.WriteLine("初始化组件开始"); await next.Invoke(content); Console.WriteLine("管道下步执行完毕"); } 做个简便的Middleware基类 虽然有约定方法,但有时候我们在开发的时候往往会犯迷糊,想不起来到底是什么样的约定,所以,在这里我们可以定义一个抽象基类,然后以后所有的Middleware在定义的时候都继承该抽象类并重载Invoke方法即可,从而可以避免约定忘记的问题。代码如下: /// <summary> /// 抽象基类 /// </summary> public abstract class AbstractMiddleware { protected RequestDelegate Next { get; set; } protected AbstractMiddleware(RequestDelegate next) { this.Next = next; } public abstract Task Invoke(HttpContext context); } /// <summary> /// 示例Middleware /// </summary> public class DemoMiddleware : AbstractMiddleware { public DemoMiddleware(RequestDelegate next) : base(next) { } public async override Task Invoke(HttpContext context) { Console.WriteLine("DemoMiddleware Start."); await Next.Invoke(context); Console.WriteLine("DemoMiddleware End."); } } 使用方法和上面的一样。 终止链式调用或阻止所有的Middleware 在有些情况下,当然根据某些条件判断以后,可能不在需要继续往下执行下去了,而是想知己诶返回结果,那么你可以在你的Middleware里忽略对 另外,在有些情况下,你可能需要实现类似之前版本中的handler的功能,即不经常任何Pipeline直接对Response进行响应,新版ASP.NET里提供了一个run方法用于实现该功能,只需要在Configure方法里调用如下代码即可实现类似的内容输出。 app.Run(async context => { context.Response.ContentType = "text/html"; await context.Response.WriteAsync("Hello World!"); }); 关于ASP.NET 5 Runtime的内容,请访问:https://msdn.microsoft.com/en-us/magazine/dn913182.aspx 遗留问题 在Mvc项目中,所有的依赖注入类型都是通过IServiceProvider实例来获取的,目前可以通过以下形式获取该实例: var services = Context.RequestServices; // Controller中 var services = app.ApplicationServices; // Startup中 获取了该实例以后,即可通过如下方法来获取某个类型的对象: var controller = (AccountController)services.GetService(typeof(AccountController)); // 要判断获取到的对象是否为null 如果你引用了Microsoft.Framework.DependencyInjection命名空间的话,还可以使用如下三种扩展方法: var controller2 = (AccountController)services.GetService<AccountController>(); // 要判断获取到的对象是否为null //如下两种方式,如果获取到的AccountController实例为null的话,就会字段抛异常,而不是返回null var controller3 = (AccountController)services.GetRequiredService(typeof(AccountController)); var controller4 = (AccountController)services.GetRequiredService<AccountController>(); 那么问题来了?如何不在Startup和Controller里就可以获取到HttpContext和IApplicationBuilder实例以便使用这些依赖注入服务? 如何获取IApplicationBuilder实例? 如何获取HttpContext实例? 引用:http://www.mikesdotnetting.com/article/269/asp-net-5-middleware-or-where-has-my-httpmodule-gone |
请发表评论