問題
專注于為中小企業(yè)提供成都網站制作、做網站服務,電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)隆化免費做網站提供優(yōu)質的服務。我們立足成都,凝聚了一批互聯網行業(yè)人才,有力地推動了上千多家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網站建設實現規(guī)模擴充和轉變。
ASP.NET Web API構建 Web應用程序時,要求使用 Session在服務器存儲一些用戶特定的信息
解決方案
ASP.NET Web API不支持 Session,因為 API根本不依賴于System.Web。他想試圖擺脫偽造 Session,非 HTTP這樣的概念。
然而,如果我們在 ASP.NET運行時中運行 ASP.NET Web API,還想啟用Session。我們可以通過兩種方式來做:
全局:應用于整個API
局部:應用于指定路由
啟用全局方式,我們需要在 Global.asax中通過SesssionStateBehavior.Required顯示的設置啟用Session行為。
protected void Application_PostAuthorizeRequest() { HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required); }
啟用指定路由(局部方式),我們可以通過過使用路由處理器,讓路由處理器繼承自IRequiresSessionState。然后,我們可以在指定的路由上附加處理器,這個就在請求指定路由的時候啟用了 Session。
工作原理
默認的 ASP.NET Web API模板,會幫我們在WebApiConfig靜態(tài)類中使用 HttpConfiguration定義默認路由,因為,框架附帶的擴展方法是支持我們使用 System.Web.RouteCollection,在定義 MVC路由的地方定義 Web API路由。
雖然 MapHttpRoute的多個重載方法經常被使用,但是這些重載方法都是void(無返回值)方法,實際上,方法還是返回了一個最新聲明路由的實例,只是方法的調用結果一般都是被拋棄掉的。在使用 Syste.Web.RouteCollection直接定義路由的情況下,返回值是 System.Web.Route的對象,我們可以將其賦值給 IrouteHandler。
當運行在 ASP.NET的時候,ASP.NET Web API框架使用同樣的機制來確保 Api請求可以準確到達,他會賦值 HttpControllerRouteHandler給每一個 Web API路由,HttpControllerRouteHandler是 GetHttpHandler方法返回的一個HttpControllerHandler實例,這是 ASP.NET Web API管道的入口點。HttpControllerHandler(WEB API的核心)雖然很復雜,但是究其原理也就是一個傳統的 IHttpAsyncHandler(舊的 IHttpHandler的一個異步的版本)。
我們可以通過實現IRequiresSessionState的接口,來強制在 IHttpHandler中使用 Session。ASP.NET將會顯示的為每一個實現了這個接口路由啟用 Session。
另外要在全局范圍內調用 HttpContext.Current.SetSessionStateBehavior方法和傳遞 SessionStateBehavior,需要為當前的HttpContext顯示的啟用 Session。SetSessionStateBehavior方法必須在 AcquireRequestState事件之前調用。
代碼演示
繼承兩個類:
HttpControllerHandler
HttpControllerRouteHandler
我們將創(chuàng)建兩個自定義類
SessionHttpControllerHandler:實現 IRequiresSessionState
SessionHttpControllerRouteHandler:只是代替默認類型,來充當返回 SessionHttpControllerHandler的工廠
如清單 1-26所示。
清單 1-26.定制HttpControllerHandler和 HttpControllerRouteHandler
public class SessionControllerHandler : HttpControllerHandler, IRequiresSessionState { public SessionControllerHandler(RouteData routeData) : base(routeData) { } } public class SessionHttpControllerRouteHandler : HttpControllerRouteHandler { protected override IHttpHandler GetHttpHandler(RequestContext requestContext) { return new SessionControllerHandler(requestContext.RouteData); } }
現在我們需要將我們的注意力從WebApiConfig類轉移到 RouteConfig類,因為我們需要執(zhí)行 RouteCollection。接下來,我們應該在創(chuàng)建路由的時候,將 SessionHttpControllerRouteHandler賦值給 RouteHandler。如清單 1-27所示。
清單 1-27. 在System.Web.RouteCollection中注冊Web API路由
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //Web API routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ).RouteHandler = new SessionHttpControllerRouteHandler(); //MVC routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }
如果想激進一點,還是有其他的方式來執(zhí)行這個功能的。不再需要跑到RouteConfig中注冊 Api的路由,而是需要使用 WebApiConfig中的 HttpConfiguration做一些必要的處理,可以同樣達對所有 Web API路由啟用 Session。
當我們通過Web API的配置注冊路由的時候,路由是被注冊在 RouteTable中,同時,使用一個單利 HttpControllerRouteHandler.Instance處理器來處理路由。這樣,我們可以讓 ASP.NET所有調用轉到Web API路由,進入到 Web API的管道。這里說到的單例其實就是一個 Lazy
清單 1-28. 配置 Session
public static class WebApiConfig { public static void Register(HttpConfiguration config) { var httpControllerRouteHandler = typeof(HttpControllerRouteHandler).GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic); if (httpControllerRouteHandler != null) { httpControllerRouteHandler.SetValue(null, new Lazy(() => new SessionHttpControllerRouteHandler(), true)); } config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.MapHttpAttributeRoutes(); } }
現在,我們需要證明這個起作用了,需要做一個簡單模擬擲骰子的ApiController例子。首先,生成一個 1到 6之間的隨機數,將 Session中上一次的值使用當前投擲的值賦值。
清單 1-29.使用Session的 ApiController簡單例子
public class DiceResult { public int NewValue { get; set; } public int LastValue { get; set; } } public class DiceController : ApiController { public DiceResult Get() { var newValue = new Random().Next(1, 7); object context; if (Request.Properties.TryGetValue("MS_HttpContext", out context)) { var httpContext = context as HttpContextBase; if (httpContext != null && httpContext.Session != null) { var lastValue = httpContext.Session["LastValue"] as int?; httpContext.Session["LastValue"] = newValue; return new DiceResult { NewValue = newValue, LastValue = lastValue ?? 0 }; } } return new DiceResult { NewValue = newValue }; } }
值得注意的是,我們剛剛獲取的HttpContext是從 HttpRequestMessage屬性字典中通過“MS_HttpContextkey”獲取的。這個比直接從System.HttpContext.Current中獲取更具可測性。
博客園:http://www.cnblogs.com/shuizhucode/
51 CTO:http://shuizhucode.blog.51cto.com/