Accessing Session Using ASP.NET Web API Accessing Session Using ASP.NET Web API asp.net asp.net

Accessing Session Using ASP.NET Web API


MVC

For an MVC project make the following changes (WebForms and Dot Net Core answer down below):

WebApiConfig.cs

public static class WebApiConfig{    public static string UrlPrefix         { get { return "api"; } }    public static string UrlPrefixRelative { get { return "~/api"; } }    public static void Register(HttpConfiguration config)    {        config.Routes.MapHttpRoute(            name: "DefaultApi",            routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",            defaults: new { id = RouteParameter.Optional }        );    }}

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication{    ...    protected void Application_PostAuthorizeRequest()    {        if (IsWebApiRequest())        {            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);        }    }    private bool IsWebApiRequest()    {        return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);    }}

This solution has the added bonus that we can fetch the base URL in javascript for making the AJAX calls:

_Layout.cshtml

<body>    @RenderBody()    <script type="text/javascript">        var apiBaseUrl = '@Url.Content(ProjectNameSpace.WebApiConfig.UrlPrefixRelative)';    </script>    @RenderSection("scripts", required: false) 

and then within our Javascript files/code we can make our webapi calls that can access the session:

$.getJSON(apiBaseUrl + '/MyApi')   .done(function (data) {       alert('session data received: ' + data.whatever);   }));

WebForms

Do the above but change the WebApiConfig.Register function to take a RouteCollection instead:

public static void Register(RouteCollection routes){    routes.MapHttpRoute(        name: "DefaultApi",        routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",        defaults: new { id = RouteParameter.Optional }    );}

And then call the following in Application_Start:

WebApiConfig.Register(RouteTable.Routes);

Dot Net Core

Add the Microsoft.AspNetCore.Session NuGet package and then make the following code changes:

Startup.cs

Call the AddDistributedMemoryCache and AddSession methods on the services object within the ConfigureServices function:

public void ConfigureServices(IServiceCollection services){    services.AddMvc();    ...    services.AddDistributedMemoryCache();    services.AddSession();

and in the Configure function add a call to UseSession:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){    app.UseSession();    app.UseMvc();

SessionController.cs

Within your controller, add a using statement at the top:

using Microsoft.AspNetCore.Http;

and then use the HttpContext.Session object within your code like so:

    [HttpGet("set/{data}")]    public IActionResult setsession(string data)    {        HttpContext.Session.SetString("keyname", data);        return Ok("session data set");    }    [HttpGet("get")]    public IActionResult getsessiondata()    {        var sessionData = HttpContext.Session.GetString("keyname");        return Ok(sessionData);    }

you should now be able to hit:

http://localhost:1234/api/session/set/thisissomedata

and then going to this URL will pull it out:

http://localhost:1234/api/session/get

Plenty more info on accessing session data within dot net core here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state

Performance Concerns

Read Simon Weaver's answer below regarding performance. If you're accessing session data inside a WebApi project it can have very serious performance consequence - I have seen ASP.NET enforce a 200ms delay for concurrent requests. This could add up and become disastrous if you have many concurrent requests.


Security Concerns

Make sure you are locking down resources per user - an authenticated user shouldn't be able to retrieve data from your WebApi that they don't have access to.

Read Microsoft's article on Authentication and Authorization in ASP.NET Web API - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

Read Microsoft's article on avoiding Cross-Site Request Forgery hack attacks. (In short, check out the AntiForgery.Validate method) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks


You can access session state using a custom RouteHandler.

// In global.asaxpublic class MvcApp : System.Web.HttpApplication{    public static void RegisterRoutes(RouteCollection routes)    {        var route = routes.MapHttpRoute(            name: "DefaultApi",            routeTemplate: "api/{controller}/{id}",            defaults: new { id = RouteParameter.Optional }        );        route.RouteHandler = new MyHttpControllerRouteHandler();    }}// Create two new classespublic class MyHttpControllerHandler    : HttpControllerHandler, IRequiresSessionState{    public MyHttpControllerHandler(RouteData routeData) : base(routeData)    { }}public class MyHttpControllerRouteHandler : HttpControllerRouteHandler{    protected override IHttpHandler GetHttpHandler(        RequestContext requestContext)    {        return new MyHttpControllerHandler(requestContext.RouteData);    }}// Now Session is visible in your Web APIpublic class ValuesController : ApiController{    public string Get(string input)    {        var session = HttpContext.Current.Session;        if (session != null)        {            if (session["Time"] == null)                session["Time"] = DateTime.Now;            return "Session Time: " + session["Time"] + input;        }        return "Session is not availabe" + input;    }}

Found here: http://techhasnoboundary.blogspot.com/2012/03/mvc-4-web-api-access-session.html


Why to avoid using Session in WebAPI?

Performance, performance, performance!

There's a very good, and often overlooked reason why you shouldn't be using Session in WebAPI at all.

The way ASP.NET works when Session is in use is to serialize all requests received from a single client. Now I'm not talking about object serialization - but running them in the order received and waiting for each to complete before running the next. This is to avoid nasty thread / race conditions if two requests each try to access Session simultaneously.

Concurrent Requests and Session State

Access to ASP.NET session stateis exclusive per session, which means that if two different users makeconcurrent requests, access to each separate session is grantedconcurrently. However, if two concurrent requests are made for thesame session (by using the same SessionID value), the first requestgets exclusive access to the session information. The second requestexecutes only after the first request is finished. (The second sessioncan also get access if the exclusive lock on the information is freedbecause the first request exceeds the lock time-out.) If theEnableSessionState value in the @ Page directive is set to ReadOnly, arequest for the read-only session information does not result in anexclusive lock on the session data. However, read-only requests forsession data might still have to wait for a lock set by a read-writerequest for session data to clear.

So what does this mean for Web API? If you have an application running many AJAX requests then only ONE is going to be able to run at a time. If you have a slower request then it will block all others from that client until it is complete. In some applications this could lead to very noticeably sluggish performance.

So you should probably use an MVC controller if you absolutely need something from the users session and avoid the unncesessary performance penalty of enabling it for WebApi.

You can easily test this out for yourself by just putting Thread.Sleep(5000) in a WebAPI method and enable Session. Run 5 requests to it and they will take a total of 25 seconds to complete. Without Session they'll take a total of just over 5 seconds.

(This same reasoning applies to SignalR).