73

I need to store some information in session(or in whatever in ASP.NET Web API) that I need to retrieve in every API request. We will have one api IIS web site and multiple web site binding will be added through host header. When any request comes in for example, api.xyz.com, host header will be checked and store that website information in session that will be used in each subsequent api request when making a call to database.

I know there is no support for session in ASP.NET Web API. Is there any other way to handle this kind of situation? Where can I store information that can be retrieving in each subsequent request?

thanks.

user1186065
  • 1,847
  • 3
  • 17
  • 22

5 Answers5

113

in Global.asax add

public override void Init()
{
    this.PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
    base.Init();
}

void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
    System.Web.HttpContext.Current.SetSessionStateBehavior(
        SessionStateBehavior.Required);
}

give it a shot ;)

Owen Blacker
  • 4,117
  • 2
  • 33
  • 70
Søren
  • 6,517
  • 6
  • 43
  • 47
  • 4
    This worked! Thank you very much! The top answer didn't work since the route.RouteHandler doesn't seem to work because it is not the same type in Web API. This answer solved the issue. – Tikkes Apr 09 '13 at 12:13
  • 15
    +1 I think it's okay to use Session in Web Api in some special cases if you know what you're doing. – nima May 28 '13 at 12:32
  • 7
    I used this and some of my pages became very slow. It's better to test if the request is a Web API: if (HttpContext.Current.Request.Url.AbsolutePath.StartsWith("/api/")) – nima Jun 01 '13 at 14:54
  • exactly, because of that it's disabled by default and better too avoid it in Web API... – Søren Jun 02 '13 at 06:52
  • @nima, can you explain a little more? Do you mean surround the call to SetSessionStateBehavior with the if statement you provided? – Sean Aug 07 '14 at 23:01
  • To answer my own question, I'm pretty sure the answer is yes. The same thing is included in the answer to this linked question: http://stackoverflow.com/questions/9594229/accessing-session-using-asp-net-web-api – Sean Aug 08 '14 at 20:54
  • 1
    How to set the session timeout for this? – Jeeva J Sep 07 '15 at 06:11
  • 1
    @nima there are some extra characters in [..]("/api")) of your comment: ‌​ – Drasive Jul 04 '17 at 11:26
  • You're right, but I cannot edit it. I just hope that anyone copy-pasting it notices that :) – nima Jul 04 '17 at 11:52
  • I have implemented in the same way you have explained, but still, session value is getting reset to null when next call comes. can you please help me out? – Chiragkumar Thakar Apr 24 '18 at 14:43
  • You should be very careful with this as you may get into serious performance issues. With SessionStateBehavior.Required ASP.NET sets exclusive locks for requests of the given user session which makes these requests be processed in sequence (no parallel processing anymore). So your performance tuning to save some database trips may finally lead to the overall performance of your WEB API to drop dramatically. My experience with Web APIs shows it is not worth it. If you really want to save some database trips, consider using MemoryCache instead of Session (but isn't it premature optimization?). – piter entity Jul 25 '18 at 10:27
  • @Soren, so the question title clearly says "ASP.NET Web API session or something", but in one of your comments above you say " it's disabled by default and better too avoid it in Web API.". So you give an answer, that does not apply to WebAPI ? – joedotnot Jul 08 '20 at 14:08
  • If you are not going to modify the Session values, it would be better to use `SessionStateBehavior.ReadOnly`, which will not block the request in Asp.Net Framework due to its default session locking behaviour – zsubzwary Aug 16 '21 at 11:38
  • This doesn't work anymore. You cannot have C# in the Global.asax file. It's now just an HTML file basically. – Aquaphor Aug 08 '22 at 07:00
92

Well, REST by design is stateless. By adding session (or anything else of that kind) you are making it stateful and defeating any purpose of having a RESTful API.

The whole idea of RESTful service is that every resource is uniquely addressable using a universal syntax for use in hypermedia links and 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 whatever you are trying to do with Web API here, should most likely be re-architectured if you wish to have a RESTful API.

With that said, if you are still willing to go down that route, there is a hacky way of adding session to Web API, and it's been posted by Imran here http://forums.asp.net/t/1780385.aspx/1

Code (though I wouldn't really recommend that):

public 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);
    }
}

public 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;
    }
}

and then add the HttpControllerHandler to your API route:

route.RouteHandler = new MyHttpControllerRouteHandler();
Owen Blacker
  • 4,117
  • 2
  • 33
  • 70
Filip W
  • 27,097
  • 6
  • 95
  • 82
  • 2
    Thanks. very helpful. I am not going against REST but I wanted to save database trip for each api request. We have unique id associated to api domain that making a request which I need to pass along to database call for each api request. If I don't use session, I end up repeating the same logic for each api request. BTW is there a way I can override similar to PreRequestHandlerExecute we have in asp.net and store data in session which I want to retrieve in each api request? – user1186065 Jul 16 '12 at 15:46
  • 6
    The controller will be tough to unit test in this manner. I would add an HttpContextBase (or HttpSessionStateBase) argument in the controller constructor and use DI to resolve it to new HttpContextWrapper(HttpContext.Current) for the application. Then in the unit tests you can mock it. – Dmitry S. Oct 05 '12 at 19:08
  • 1
    As @Filip said, let's do not follow this path (adding session to Web API which is supposed to be stateless). But the same Filip has a good article about Web API Caching: http://goo.gl/2rnfI I forked him in gitHub and add a few more functionality to the source code, now it supports separated output cache for logged-in users: http://goo.gl/oW4hX NOTE: I'm still improving the code and test it for any possible security breach. – Tohid Dec 28 '12 at 15:26
  • 6
    I find Web Api controllers to be very useful in that I can set a model or view model as a return type and it works nicely with my javascript frameworks, so I don't have to create JsonResults in ordinary controllers. In this sense, they're not true, stateless REST API controllers, but a more natural endpoint for AJAX requests, and so session state is still something I'd like to have. – user888734 Jan 27 '14 at 20:18
  • Where this is very useful for REST is where you authenticate the user on the first request and then use the authentication state to know that they are authenticated on all subsequent requests. – David Thielen Feb 02 '15 at 23:51
  • `route.RouteHandler = new MyHttpControllerRouteHandler();` is exactly what I needed to enable session inside of areas because I needed to override IRouteHandler.. – petrosmm Sep 27 '18 at 19:12
35

In WebApi 2 you can add this to global.asax

protected void Application_PostAuthorizeRequest() 
{
    System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}

Then you could access the session through:

HttpContext.Current.Session
Dalorzo
  • 19,834
  • 7
  • 55
  • 102
  • 3
    How does this add anything to the other answers? – eddie_cat Jan 21 '15 at 14:22
  • @eddie_cat simply put, he is putting it in PostAuthorize. Still, to make this better, he could point that out AND add the conditional check if the request is an api request as nima points out. – Suamere Apr 29 '15 at 21:07
  • Will this answer have the effect of serializing all requests to Web.API (i.e. a long running method needs to return before the next one is allowed to execute ) ? – joedotnot Jul 08 '20 at 14:13
  • No, serialization is a different setting on the global asax. Although, your clases must be serializable in order to be stored in sesssion – Dalorzo Jul 08 '20 at 17:05
3

You can use cookies if the data is small enough and does not present a security concern. The same HttpContext.Current based approach should work.

Request and response HTTP headers can also be used to pass information between service calls.

Dmitry S.
  • 8,373
  • 2
  • 39
  • 49
  • 1
    I know this is old, but could help to anyone else reading about this stuff. There's a limitation about the use of cookies with IE8/9 if you're using XDomainRequest to perform CORS ajax requests. You can read more about it [here](http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx) – elvin Sep 15 '14 at 23:52
2

Now in 2017 with ASP.Net Core you can do it as explained here.

The Microsoft.AspNetCore.Session package provides middleware for managing session state.

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
  // Adds a default in-memory implementation of IDistributedCache.
    services.AddDistributedMemoryCache();

    services.AddSession(options =>
    {
        // Set a short timeout for easy testing.
        options.IdleTimeout = TimeSpan.FromSeconds(10);
        options.Cookie.HttpOnly = true;
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseSession();
}

From the Docs: Introduction to session and application state in ASP.NET Core

Already tested on a working project

CREM
  • 1,929
  • 1
  • 25
  • 35