本文將分為上下兩部分您也可以從《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的實例
From:http://tw.wingwit.com/Article/program/net/201311/13946.html