问题

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 管道的入口点。HttpControllerHandler(WEB API 的核心)虽然很复杂,但是究其原理也就是一个传统的 IHttpAsyncHandler(旧的 IHttpHandler 的一个异步的版本)。

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

另外要在全局范围内调用 HttpContext.Current.SetSessionStateBehavior 方法和传递 SessionStateBehavior,需要为当前的 HttpContext 显示的启用 Session。SetSessionStateBehavior方法必须在 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 到6 之间的随机数,将 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中获取更具可测性。

[水煮 ASP.NET Web API2 方法论](1-8)添加 Session 状态的更多相关文章

  1. [水煮 ASP.NET Web API2 方法论](3-9)空气路由的设置

    阅读导航 问题 解决方案 工作原理 代码演示 在此解释一下,空气路由,是本人臆想出来,觉着更能表达 IgnoreRoute 的意图,如果看着辣眼睛^^,请见谅. 问题 我们在之定义过集中式路由,集中式 ...

  2. [水煮 ASP.NET Web API2 方法论](1-1)在MVC 应用程序中添加 ASP.NET Web API

    问题 怎么样将 Asp.Net Web Api 加入到现有的 Asp.Net MVC 项目中 解决方案 在 Visual Studio 2012 中就已经把 Asp.Net Web Api 自动地整合 ...

  3. [水煮 ASP.NET Web API2 方法论](1-5)ASP.NET Web API Scaffolding(模板)

    问题 我们想快速启动一个 ASP.NET Web API 解决方案. 解决方案 APS.NET 模板一开始就支持 ASP.NET Web API.使用模板往我们的项目中添加 Controller,在我 ...

  4. [水煮 ASP.NET Web API2 方法论](3-8)怎样给指定路由配置处理器

    阅读导航 问题 解决方案 工作原理 代码演示 问题 如果仅仅针对指定的路由进行某些特定的消息处理,而不是应用于所有路由,我们应该怎么做呢? 解决方案 ASP.NET WEB API 的很多功能都内建了 ...

  5. [水煮 ASP.NET Web API2 方法论](3-7)默认 Action 请求方式以及 NonActionAttribute

    问题 在 Controller 中有一个 public 的方法,但是又不想将这个 publlic 方法暴露成为一个 API. 解决方案 ASP.NET Web API 中,正常是通过 HTTP 谓词来 ...

  6. [水煮 ASP.NET Web API2 方法论](3-6)万能路由

    问题 定义什么样的路由,可以不会受请求参数类型和数量的限制,而被全部捕获? 解决方案 在路由模板中,给参数添加一个"*"前缀,例如 {*param},只要请求的 URL 能够和路由 ...

  7. [水煮 ASP.NET Web API2 方法论](3-5)路由约束

    问题 怎么样限制路由中参数的值. 解决方案 ASP.NET WEB API 允许我们通过 IHttpRouteConstraint 接口设置路由约束.集中式路由和直接式路由都可以使用 IHttpRou ...

  8. [水煮 ASP.NET Web API2 方法论](3-4)设置路由可选项

    问题 怎么样创建一个路由,不管客户端传不传这个参数,都可以被成功匹配. 解决方案 ASP.NET WEB API 的集中式路由和属性路由都支持路由声明可选参数. 在用集中式路由中可以通过 RouteP ...

  9. [水煮 ASP.NET Web API2 方法论](3-3)路由默认值

    问题 如何为路由中参数设置默认值. 解决方案 不管使用属性路由还是集中式路由,ASP.NET WEB API 都可以很方便的为路由定义默认参数.在每次客户端请求的时候,如果客户端没有传这些参数,框架会 ...

随机推荐

  1. redis中如何对 key 进行分类

    因为redis中的 hash是不支持设置过期时间的,如果我们要 设置过期时间,还要分类存储,可以用下面折中的方法 其实就是我们把 key 定义的有规律一些,通过在key的字符串内部 分类,上图只是因为 ...

  2. ListBox, ListView, GridView

    ListView是ListBox的派生类,而GridView是ViewBase的派生类 ListView的View属性是ViewBase,所以GridView可以作为ListView的属性 如 < ...

  3. php中error_reporting

    error_reporting(255);是列出所有提示error_reporting(0);是不显示所有提示建议使用error_reporting(7);只显示严重错误1 E_ERROR 致命的运行 ...

  4. footer点击添加active class

    //footer点击添加active class var indexFooter= document.querySelectorAll('#index_footer li'); for (var i ...

  5. MongoDB入门(5)- 我们自己封装的MongoDB-Java版本

    用法 实体定义 package com.wisdombud.mongotool; import java.io.Serializable; import java.util.Date; import ...

  6. tcp/ip 学习-通过视频学习

    视频下载地址:http://down.51cto.com/zt/5518/ http://www.icoolxue.com/album/show/328 每天可以拿两个番茄钟看视频,主要目的还是了解, ...

  7. [洛谷P2023] [AHOI2009]维护序列

    洛谷题目链接:[AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列 ...

  8. 【C++ STL】Deques

    1.结构 容器deque和vector非常相似,也是采用动态数组来管理元素,提供随机存取,有着和vector几乎一样的接口,不同的是deque的动态数组头尾都开放,因此可以在头尾都可以进行快速的安插和 ...

  9. PowerDesigner16 活动图

    活动是某件事情正在进行的状态.活动在状态机中表现为一个由一系列动作组成 的非原子的执行过程. 活动图是一种描述系统行为的图,它用于展现 参与行为的实体所进行的各种活动的顺序关系.活动图(Activit ...

  10. PowerDesigner16 用例图

    用例图主要用来描述角色以及角色与用例之间的连接关系.说明的是谁要使用系统,以及他们使用该系统可以做些什么.一个用例图包含了多个模型元素,如系统.参与者和用例,并且显示这些元素之间的各种关系,如泛化.关 ...