【问题标题】:How do I implement session aware WEB API on specific controllers?如何在特定控制器上实现会话感知 WEB API?
【发布时间】:2015-06-07 03:43:04
【问题描述】:

我正在创建一些需要会话感知的 WEB API 2 控制器。我以前通过添加

来做到这一点
/// <summary>
/// Application_s the post authorize request.
/// </summary>
protected void Application_PostAuthorizeRequest()
{
     HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}

但是,我们在解决方案中为站点的其他业务关键部分提供了 API 控制器,这些部分已经过高度优化,并且返回的响应大约为 500 毫秒,如果启用此功能,则持续时间最长可达 2 秒。这些控制器不需要会话感知。

我们只需要给定会话访问权限的某些控制器,我已经阅读了这篇文章 http://www.codeproject.com/Tips/513522/Providing-session-state-in-ASP-NET-WebAPI,并且正在考虑是否可以添加具有会话感知能力的不同路由,但映射时没有 RouteHandler 属性路线。

有人有什么想法吗?

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-web-api


    【解决方案1】:

    请记住,使用 SessionStateBehavior.Required 会产生相当大的影响,并且只有部分路由实际上需要对会话的写入权限。最糟糕的是每个用户一次只能处理一个请求,因为会话必须被锁定。

    话虽如此,有一种方法可以根据路线以不同的方式处理会话。

    您可以使用IHttpRoute.DataTokens 将自定义字段添加到您的路线。 我创建了一个小的扩展类来分别为每个路由设置 SessionStateBehavior:

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

    在设置路由时,您可以使用扩展来定义特定的会话状态行为:

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

    然后,在PostAuthorizeRequest 事件中,您可以解析您的路由并相应地设置 SessionStateBehavior:

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

    【讨论】:

    • 感谢您的回答,但我的解决方案运行良好。只有对 sessionapi 路由的 API 调用需要会话,并且由于有 2 种路由配置,可以调用其中任何一种,并且可以从调用脚本轻松切换。
    • 是的,如果 API 控制器使用特定的路由模板,您的解决方案将授予对会话的完全访问权限。我只是想指出,可能并不总是需要完全的写访问权限,因为 ASP.Net 独占锁定会话:msdn.microsoft.com/en-us/library/ms178581.aspx“但是,如果对同一会话发出两个并发请求,则第一个请求将获得对会话的独占访问权限信息。第二个请求只有在第一个请求完成后才会执行。"
    • 实际上,您可以为每个会话状态行为创建路由模板,例如“/readonlysessionapi/....”,它也可以正常工作。但是,您真的希望客户必须关心您的会话行为吗?假设您更改了服务器端代码,并且现在某个操作需要对会话的写入权限,您将不得不更新所有客户端,因为路由发生了变化。
    • 如果你使用路由属性怎么办?
    • @petrosmm 我同时使用 WebAPI 和 MVC,并且我使用会话状态来帮助保护我的防伪令牌。只要 [SessionState(SessionStateBehavior.ReadOnly) 处于打开状态,Web API 就能够很好地验证 MVC 设置的令牌。
    【解决方案2】:

    我想出了一个可行的解决方案。我在路线注册期间添加了第二条路线,例如

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

    然后在 global.asax.cs 我有

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

    这允许任何控制器在会话感知或不从客户端发出请求,从而解决我的问题。

    我的解决方案在配置常量等方面更简洁一些,但上面的代码就是示例。

    【讨论】:

      猜你喜欢
      • 2016-02-17
      • 2018-04-23
      • 2018-07-16
      • 2012-11-12
      • 2015-12-30
      • 1970-01-01
      • 2013-10-18
      • 2015-04-25
      • 1970-01-01
      相关资源
      最近更新 更多