Adding Session support to ASP.NET Web API

56 篇文章 0 订阅

First the disclaimer. Yes, there are a lot of problems with using session in your ASP.NET applications. Moreover, by default, HTTP (and by extension, REST) is stateless – and as a result each HTTP request should carry enough information by itself for its recipient to process it to be in complete harmony with the stateless nature of HTTP.

So if you are designing a proper API, if you are a REST purist, or if you are Darrel Miller, you definitely do not want to continue reading this article. But if not, and you find yourself in a scenario requiring session – perhaps you are using Web API to facilitate your JS or MVC application, and you want to sync the state of Web API with the state of MVC easily, or you simply want a quick and easy way to persist state on the server side – this article is for you.

More after the jump.

 

Adding session support with IRequiresSessionState

Let’s start by exploring how to extend Routes with Session state.

Web API routing, is operating on the same underlying ASP.NET RouteCollection, and therefore similar principles apply. ASP.NET has a concept of IRouteHandler which is a property on a System.Web.Routing.Route class and is responsible for processing HTTP requests for a route. By default, all Web API routes use HttpControllerRouteHandler which doesn’t do much, except handing over the HttpContexBase to HttpControllerHandler.

In order to provide session support, we will need to create 2 custom components:
IRouteHandler, to replace the default HttpControllerRouteHandler and to tell the ASP.NET pipeline to go to our custom HttpControllerHandler.
– customized HttpControllerHandler, which will mark the route as being session enabled and then go back to the Web API execution pipeline

So what now? That’s surprisingly easy.

In order to enforce session on the IHttpHandler, we simply implement the interface IRequiresSessionState (more about which you can read here). This is a so-called marker interface – so all you need to do is make the handler use it, and you don’t have to implement any methods.

C#

public class SessionControllerHandler : HttpControllerHandler, IRequiresSessionState { public SessionControllerHandler(RouteData routeData) : base(routeData) {} }

1

2

3

4

5

6

public class SessionControllerHandler : HttpControllerHandler, IRequiresSessionState

{

        public SessionControllerHandler(RouteData routeData)

            : base(routeData)

        {}

}

 

This class does nothing more than introduce the marker interface, and hands the rest of the work to the default HttpControllerHandler which then continues with Web API execution pipeline.

By the way – this solution is inspired by the original proposal by Imran Baloch, who is doing great job for the ASP.NET community so make sure to follow his blog.

Plugging this in

So now we have session enabled, but how to plug this in in the first place?

Well, two steps:
1) create IRouteHandler that points to our new SessionControllerHandler
2) add it to the route when we register routes, so that a given route would use it

 

C#

public class SessionRouteHandler : IRouteHandler { IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) { return new SessionControllerHandler(requestContext.RouteData); } }

1

2

3

4

5

6

7

public class SessionRouteHandler : IRouteHandler

{

    IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)

    {

       return new SessionControllerHandler(requestContext.RouteData);

    }

}

 

And now, in the route registration, we add our route handler:

C#

RouteTable.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ).RouteHandler = new SessionRouteHandler();

1

2

3

4

5

RouteTable.Routes.MapHttpRoute(

name: "DefaultApi",

routeTemplate: "api/{controller}/{id}",

defaults: new { id = RouteParameter.Optional }

).RouteHandler = new SessionRouteHandler();

 

Note, that I’m registering this against RouteTable.Routes directly, rather than GlobalConfiguration.Configuration.Routes. The reason for this is that the first collection is of type System.Web.Routing.Route (same as MVC) while the latter is of type System.Web.Http.Routing.IHttpRoute, and it does not allow you to set RouteHandler on it. Basically, this is us taking leverage on the fact that I mentioned in the beginning – that Web API routes and MVC routes are ultimately part of the same route collection.

Putting it to use

Now we are ready to go, and let’s create a simple Session wrapper to test our solution – you can use whatever you want, any session abstraction you like, this is purely for demonstration:

C#

public interface IDataPersistance<T> { T ObjectValue { get; set; } } public class SessionDataPersistance<T> : IDataPersistance<T> where T : class { private static string key = typeof(T).FullName.ToString(); public T ObjectValue { get { return HttpContext.Current.Session[key] as T; } set { HttpContext.Current.Session[key] = value; } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

    public interface IDataPersistance<T>

    {

        T ObjectValue { get; set; }

    }

 

    public class SessionDataPersistance<T> : IDataPersistance<T>

      where T : class

    {

        private static string key = typeof(T).FullName.ToString();

 

        public T ObjectValue

        {

            get

            {

                return HttpContext.Current.Session[key] as T;

            }

            set

            {

                HttpContext.Current.Session[key] = value;

            }

        }

    }

 

Now we can inject this into the controller, and use session in the GET action.

C#

public class TeamsController : ApiController { private IDataPersistance<Team> _teamStorage; private ITeamService _teamService; public TeamsController() : this(new SessionDataPersistance<Team>(), new TeamService()) {} //THIS IS TO AVOID WIRING DI IN THE DEMO public TeamsController(IDataPersistance<Team> teamStorage, ITeamService service) { _teamStorage = teamStorage; _teamService = service; } public Team Get(int id) { //GET FROM SESSION Team t = _teamStorage.ObjectValue; //IF NULL, SET IN SESSION if (t == null) { t = _teamService.Get(id); _teamStorage.ObjectValue = t; } return t; } }

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

29

30

31

public class TeamsController : ApiController

{

private IDataPersistance<Team> _teamStorage;

private ITeamService _teamService;

 

        public TeamsController()

            : this(new SessionDataPersistance<Team>(), new TeamService())

        {}

//THIS IS TO AVOID WIRING DI IN THE DEMO

 

        public TeamsController(IDataPersistance<Team> teamStorage, ITeamService service)

        {

_teamStorage = teamStorage;

_teamService = service;

        }

 

        public Team Get(int id)

        {

            //GET FROM SESSION

            Team t = _teamStorage.ObjectValue;

 

            

            //IF NULL, SET IN SESSION

            if (t == null)

            {

                t = _teamService.Get(id);

                _teamStorage.ObjectValue = t;

            }

            return t;

        }

}

 

That’s it! Quick, easy and simple. We can test – on the 1st request, the object from session is obviously null.

 

Then, on the next one, it’s pulled from session:

 

Summary

There are probably many reasons why you DON’T want to use session.

At the same time, there are many scenarios when using session is the most viable solution and makes most business sense, especially as you don’t design pure API but rather use Web API to facilitate your MVC app – and you need i.e. a quick way to share artefacts between MVC and Web API.

While it’s not supported out of the box, with this approach you can quickly enable it without any problems.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值