asp.net

位置:IT落伍者 >> asp.net >> 浏览文章

为ASP.NET MVC扩展异步Action功能(上)


发布日期:2018年10月26日
 
为ASP.NET MVC扩展异步Action功能(上)

本文将分为上下两部分您也可以从《Extend ASPNET MVC for Asynchronous Action》获得全部内容

异步请求处理是ASPNET 中引入的高级特性它依托IO Complete Port对于提高IO密集型应用程序的吞吐量非常重要(详见原理描述和性能测试)但是目前ASPNET MVC框架缺少异步Action功能这也就是老赵经常挂在嘴边的那个目前ASPNET MVC所缺少的非常重要的功能在TechED China的Session中我曾经给出过一个所谓的解决方案但是它复杂性之高使那个解决方案有太多限制为了弥补TechED上的遗憾以及准备NET开发大会上的ASPNET MVC最佳实践的Session我在春节休假期间仔细思考了一下这方面的问题得出了一个相对不错的扩展完整方便并且非常轻巧——核心逻辑代码只有行左右这意味着绝大部分功能将会委托给框架中现成的内容确保了扩展的稳定高效并且拥有较好的向后兼容性

值得一提的是我在/号便基于ASPNET MVC的Beta版本写出了这个扩展的第一个版本而在不久之后微软发布了ASPNET MVC RC我在移植解决方案的过程中发现ASPNET MVC RC在框架设计上进行了较大的改进这使得我在构建扩展时的策略发生了些许变化令人欣喜的是RC版本的这些变化对于构建一个扩展尤其是现在这种低端级别的扩展变得更加容易ASPNET MVC框架实现了它到处可扩展的承诺

那么我们现在就来详细分析一下这个扩展的实现方式

请求处理方式的改变

在制定基本改造策略之前我们需要了解ASPNET MVC框架目前的架构及请求处理流程如下

在应用程序启动时(此时还没有接受任何请求)将针对MVC请求的Route策略注册至ASPNET Routing模块此时每个Route策略(即Route对象)中的RouteHandler属性为ASPNET MVC框架中的MvcRouteHandler

当ASPNET Routing模块接收到一个匹配某个Route策略的HTTP请求时将会调用该Route对象中RouteHandler对象的GetHttpHandler以获取一个HttpHandler并交由ASPNET执行MvcRouteHandler永远将返回一个MvcHandler对象

MvcHandler在执行时将取出RouteData中的controller值并以此构建一个实现了IController接口的控制器对象并调用IController接口的Execute方法执行该控制器

对于一个ASPNET MVC应用程序来说大部分控制器将会继承SystemWebMvcController类型Controller类将会从RouteData获取action值并交给实现IActionInvoker接口的对象来执行一个Action

……

如果我们要将这个流程改造成异步处理那么就要让它符合ASPNET架构中的异步处理方式ASPNET架构对于异步请求的处理可以体现在好几种方式上例如异步页面异步Http Module等而最适合目前场合的做法自然是异步Http Handler为实现一个异步Handler我们需要让处理请求的Handler实现IHttpAsyncHandler接口而不是传统的IHttpHandler接口IHttpAsyncHandler接口中的BeginProcessRequest和EndProcessRequest两个方法构成了NET中的APM(Aynchronous Programming Model异步编程模型)模式可以使用二段式的异步调用来处理一个HTTP请求

您应该已经发现如果我们要支持异步Action就必须根据当前的请求信息来确认究竟是执行一个IHttpHandler对象还是IHttpAsyncHandler对象而在ASPNET MVC框架在默认情况下是在Http Handler(即MvcHandler对象)内部进行控制器的检查构造和调用这为时已晚我们必须讲这些逻辑提前到Routing过程中才行幸运的是ASPNET Routing所支持的IRouteHandler就像是ASPNET中的IHttpHandlerFactory可以根据情况生成不同的Handler来执行因此我们只要构建一个新的IRouteHandler类型即可

于是就诞生了AsyncMvcRouteHandler——可以想象的出其中的部分代码与框架中的MvcHandler相同因为在一定程度上我们的确只是把原本在MvcHandler里做的事情给提前了

public class AsyncMvcRouteHandler : IRouteHandler

{

public IHttpHandler GetHttpHandler(RequestContext requestContext)

{

string controllerName = requestContextRouteDataGetRequiredString(controller);

var factory = ControllerBuilderCurrentGetControllerFactory();

var controller = factoryCreateController(requestContext controllerName);

if (controller == null)

{

throw new InvalidOperationException();

}

var coreController = controller as Controller;

if (coreController == null)

{

return new SyncMvcHandler(controller factory requestContext);

}

else

{

string actionName = requestContextRouteDataGetRequiredString(action);

return IsAsyncAction(coreController actionName requestContext)

(IHttpHandler)new AsyncMvcHandler(coreController factory requestContext) :

(IHttpHandler)new SyncMvcHandler(controller factory requestContext);

}

}

internal static bool IsAsyncAction(

Controller controller string actionName RequestContext requestContext)

{

}

}

在GetHttpHandler方法中我们先从RouteData的controller字段中获取控制器的名字并通过注册在ControllerBuilder上的Factory来创建一个实现了IController接口的控制器对象由于我们需要使用Controller类中包含的ActionInvoker来辅助检测Action的异步需求因此我们会设法将其转化为Controller类型如果转换成功就会取出RouteData中的action字段的值并通过IsAsyncAction方法来确认当前Action是否应该异步执行如果是则返回一个实现了IHttpAsyncHandler的AsyncMvcHandler对象否则就返回一个实现IHttpHandler的SyncMvcHandler对象

至于AsyncMvcRouteHandler的使用只需在MapRoute时将Route Handler重新设置一下即可

public static void RegisterRoutes(RouteCollection routes)

{

routesIgnoreRoute({resource}axd/{*pathInfo});

routesMapRoute(

Default // Route name

{controller}/{action}/{id} // URL with parameters

new { controller = Home action = Index id = } // Parameter defaults

)RouteHandler = new AsyncMvcRouteHandler();

}

检查是否为异步Action

从上面的代码中我们已经形成了一个约定如果要执行一个异步Action那么控制器对象必须为Controller类型这个约定的目的是为了使用Controller类中包含的IActionInvoker——确切地说是ControllerActionInvoker类型里的功能因此另一个约定便是Controller的ActionInvoker对象必须返回一个ControllerActionInvoker的实例

上一篇:ASP.NET下文件批量下载应用

下一篇:ASP.NET中实现模版的动态加载