4

I am creating some WEB API 2 controllers that are needing to be session aware. I have previous done this by adding

/// <summary>
/// Application_s the post authorize request.
/// </summary>
protected void Application_PostAuthorizeRequest()
{
     HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}

However we have API controllers in the solution for other business critical parts of the site that have been highly optimized and are returning responses of around 500ms and if this is turned on it consistently goes up to 2 seconds. These controllers do not need session awareness.

We only need certain controllers given session access, I've read this article http://www.codeproject.com/Tips/513522/Providing-session-state-in-ASP-NET-WebAPI and was thinking if it's possible to add a different route with session awareness but there is no RouteHandler property when mapping the routes.

Does anyone have any ideas?

2 Answers 2

5

Keep in mind that working with SessionStateBehavior.Required has quite an impact and only part of your routes actually need write access to the session. The worst thing is that only a single request per user will be processed at once because the session has to be locked.

That being said, there is a way to work with sessions differently depending on the route.

You can use IHttpRoute.DataTokens to add custom fields to your routes. I created a small extension class to set the SessionStateBehavior for each route, individually:

public static class SessionHelper
{
  private static SessionStateBehavior GetSessionStateBehavior(IDictionary<string, object> dataTokens)
  {
    return dataTokens.ContainsKey("SessionStateBehavior") ? (SessionStateBehavior)dataTokens["SessionStateBehavior"] : SessionStateBehavior.Default;
  }

  public static SessionStateBehavior GetSessionStateBehavior(this IHttpRoute route)
  {
    return GetSessionStateBehavior(route.DataTokens);
  }

  public static SessionStateBehavior GetSessionStateBehavior(this RouteData routeData)
  {
    return GetSessionStateBehavior(routeData.DataTokens);
  }

  public static void SetSessionStateBehavior(this IHttpRoute route, SessionStateBehavior behavior)
  {
    route.DataTokens["SessionStateBehavior"] = behavior;
  }

  public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, SessionStateBehavior behavior)
  {
    return MapHttpRoute(routes, name, routeTemplate, defaults, null, behavior);
  }

  public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, SessionStateBehavior behavior)
  {
    var route = routes.CreateRoute(routeTemplate, defaults, constraints);
    SetSessionStateBehavior(route, behavior);
    routes.Add(name, route);

    return route;
  }
}

When setting up your route, you can use the extension to define a specific session state behavior:

config.Routes.MapHttpRoute(
    "DefaultSessionApi",
    "api/{controller}/{id}",
    new { id = RouteParameter.Optional },
    SessionStateBehavior.ReadOnly);

config.Routes.MapHttpRoute(
    "WriteStuffToSession",
    "api/writestufftosession",
    null,
    SessionStateBehavior.Required);

Then, in the PostAuthorizeRequest event, you can parse your route and set the SessionStateBehavior accordingly:

protected void Application_PostAuthorizeRequest()
{
  var context = new HttpContextWrapper(HttpContext.Current);
  var path = context.Request.AppRelativeCurrentExecutionFilePath;
  if (path == null || !path.StartsWith("~/api"))
  {
    return;
  }

  var routeData = RouteTable.Routes.GetRouteData(context);
  if (routeData != null)
  {
    context.SetSessionStateBehavior(routeData.GetSessionStateBehavior());
  }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the answer but my solution is working perfectly. Only the API calls to the sessionapi route are needing the session and as there's 2 route configurations either one can be called and easily switched from the calling script.
Yes, your solution grants api controllers full access to the session if they use a specific route template. I just wanted to point out that full write access might not always be desired because ASP.Net locks the session exclusively: msdn.microsoft.com/en-us/library/ms178581.aspx "However, if two concurrent requests are made for the same session, the first request gets exclusive access to the session information. The second request executes only after the first request is finished."
In fact, you could create route templates for each session state behavior, e.g. "/readonlysessionapi/...." and it would work just as well. However, do you really want the clients having to care about your session behavior? Assume you change your server side code and an action now requires write access to the session, you would have to update all your clients because the route changes.
What if you're using Route attributes?
@petrosmm I am using both WebAPI and MVC and I use session state to help secure my anti forgery tokens. The Web API is able to validate the MVC-set tokens just fine as long as [SessionState(SessionStateBehavior.ReadOnly) is on.
|
3

I came up with a solution which worked. I have added a second route during the routes registration such as

        config.Routes.MapHttpRoute(
            name: "DefaultSessionApi",
            routeTemplate: "sessionapi/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional });

Then in the global.asax.cs I have

    /// <summary>
    /// Application_s the post authorize request.
    /// </summary>
    protected void Application_PostAuthorizeRequest()
    {
        if (HttpContext.Current.Request.FilePath.StartsWith("/sessionapi"))
        {
            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
        }
    }

This allows any controller to be request with session awareness or without from the client therefore solving my issue.

My solution is a little neater with config constants and such but this above code is the example.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.