Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement session aware WEB API on specific controllers?

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?

like image 789
Scott Reed Avatar asked Feb 10 '23 12:02

Scott Reed


1 Answers

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());
  }
}
like image 195
Axel Wilczek Avatar answered Feb 13 '23 04:02

Axel Wilczek