zoukankan      html  css  js  c++  java
  • [水煮 ASP.NET Web API2 方法论](1-8)添加 Session 状态

    问题

    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 行为。

     

    1
    2
    3
    4
    protected void Application_PostAuthorizeRequest()
    {
        HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
    }

     

     

    启用指定路由(局部方式),我们可以通过过使用路由处理器,让路由处理器继承自 IRequiresSessionState。然后,我们可以在指定的路由上附加处理器,这个就在请求指定路由的时候启用了 Session

    工作原理

    默认的 ASP.NET Web API 模板,会帮我们在 WebApiConfig 静态类中使用 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 管道的入口点。HttpControllerHandlerWEB API 的核心)虽然很复杂,但是究其原理也就是一个传统的 IHttpAsyncHandler(旧的 IHttpHandler 的一个异步的版本)。

    我们可以通过实现IRequiresSessionState 的接口,来强制在 IHttpHandler 中使用 SessionASP.NET 将会显示的为每一个实现了这个接口路由启用 Session

    另外要在全局范围内调用 HttpContext.Current.SetSessionStateBehavior 方法和传递 SessionStateBehavior,需要为当前的 HttpContext 显示的启用 SessionSetSessionStateBehavior方法必须在 AcquireRequestState 事件之前调用。

    代码演示

    继承两个类:

    • HttpControllerHandler

    • HttpControllerRouteHandler

    我们将创建两个自定义类

    • SessionHttpControllerHandler:实现     IRequiresSessionState

    • SessionHttpControllerRouteHandler:只是代替默认类型,来充当返回     SessionHttpControllerHandler 的工厂

    如清单 1-26 所示。

    清单 1-26. 定制 HttpControllerHandler HttpControllerRouteHandler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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 类,因为我们需要执行RouteCollection。接下来,我们应该在创建路由的时候,将 SessionHttpControllerRouteHandler 赋值给RouteHandler。如清单 1-27 所示。

    清单 1-27.  System.Web.RouteCollection 中注册 Web API 路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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 }
        );
    }

    如果想激进一点,还是有其他的方式来执行这个功能的。不再需要跑到 RouteConfig 中注册 Api 的路由,而是需要使用 WebApiConfig 中的 HttpConfiguration 做一些必要的处理,可以同样达对所有  Web API 路由启用Session

    当我们通过 Web API 的配置注册路由的时候,路由是被注册在 RouteTable 中,同时,使用一个单利HttpControllerRouteHandler.Instance 处理器来处理路由。这样,我们可以让 ASP.NET 所有调用转到Web API路由,进入到 Web API 的管道。这里说到的单例其实就是一个 Lazy<HttpControllerRputeHandler>。我们可以在应用程序启动的地方使用自己的类似 SessionHttpControllerRouteHandler 的类实例,然后继续注册路由到HttpConfiguration,同时,这样可以确保每一个 Web API 路由都使用了SessionHttpControllerRouteHandler,也就是说所有的路由都可以访问Session。这个简单的代码如清单 1-28 所示。

     

    清单 1-28. 配置 Session

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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<HttpControllerRouteHandler>(() => new SessionHttpControllerRouteHandler(),
                true));
            }
            config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
            );
            config.MapHttpAttributeRoutes();
        }
    }

    现在,我们需要证明这个起作用了,需要做一个简单模拟掷骰子的 ApiController 例子。首先,生成一个 1 之间的随机数,将 Session 中上一次的值使用当前投掷的值赋值。

    清单 1-29. 使用 Session  ApiController 简单例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class DiceResult
    {
        public int NewValue { getset; }
        public int LastValue { getset; }
    }
    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_HttpContext”获取的。这个比直接从 System.HttpContext.Current中获取更具可测性。

  • 相关阅读:
    技术学习博2
    正则匹配两个特定词组中间的内容的写法
    springboot mitre HTTP Proxy Servlet POST hang timeout 不执行的解决方案
    JAVA实现的HTTP反向代理 [smiley-http-proxy-servlet]学习
    get方式url传递map的写法,control传入map数据url格式&params[key]=value&params[key2]=value2
    angular7 学习笔记记录--1. TypeScript基础
    Operator '>' cannot be applied to types 'boolean' and 'number'?
    记录在linux环境redis如何重启版本5.x
    ElasticSearch (with docker )入门学习
    Dataway【实测】,让服务飞起来,让SpringBoot不再需要Controller、Service、DAO、Mapper
  • 原文地址:https://www.cnblogs.com/shuizhucode/p/6072324.html
Copyright © 2011-2022 走看看