在ASP.Net MVC中每个请求禁用Session状态

51
我正在ASP.Net MVC中创建一个ActionResult以提供图片服务。启用会话状态后,IIS将仅处理来自同一用户的一个请求。这不仅适用于MVC。因此,在调用此操作的多个图像所在页面上,只能同时处理一个图像请求。它是同步的。
我希望这个图像Action是异步的 - 我希望多个图像请求可以每个执行而不需要前一个完成。(如果这些图像只是静态文件,则IIS将以这种方式提供它们。)
因此,我想禁用那个Action的会话,或指定某些请求没有会话状态。有人知道在MVC中如何实现吗?谢谢!

https://dev59.com/6nE95IYBdhLWcg3wluwg - Mauricio Scheffer
9个回答

57

这是一个很棒的属性,但它只适用于MVC3+。 - user172163

37

不要为此实现一个动作筛选器,为什么不实现一个 RouteHandler 呢?

这里的问题是 - IRouteHandler 只有一个方法 - GetHttpHandler。当你向控制器发送一个 ASP.Net MVC 请求时,默认情况下,路由引擎通过创建 MvcRouteHandler 的新实例来处理请求,该实例返回一个 MvcHandlerMvcHandler 是标记有(惊喜!)IRequiresSessionState 接口的 IHttpHandler 实现。这就是为什么普通请求使用 Session 的原因。

如果按照我的博客文章实现用于提供图像的自定义 RouteHandler(而不是使用 MvcRouteHandler)- 你可以跳过返回带有 Session 标签的 IHttpHandler

这应该使 IIS 不再强加同步功能于你。 这也可能更高效,因为它跳过了处理过滤器的所有 MVC 代码的层。


哇,太棒了的博客文章。看起来使用标准MVC控制器,Session依赖不可避免..? - Matt Sherman
是的。幸运的是,他们使MVC尽可能可扩展,因此绕过任何不喜欢的东西并不需要太多工作。 - womp
谢谢!当我关闭会话并返回图像时,使用此操作的页面上可能会混合结果(可能有多个图像)。 - Victor Gelmutdinov
@rwalter,您现在可以替换您的评论了。已用存档链接替换了原链接。 - Dbl
3
值得了解的是:http://www.c-sharpcorner.com/UploadFile/ff2f08/session-state-behavior-per-action-in-Asp-Net-mvc/ - Dbl
显示剩余2条评论

9
我也遇到了同样的问题,经过研究和开发,这个链接对我有效。参考: https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
  1. 创建自定义属性
  2. 覆盖DefaultControllerFactory类中的“GetControllerSessionBehavior”方法。
  3. 在global.aspx中注册它

1> 创建自定义属性

public sealed class ActionSessionStateAttribute : Attribute
    {
            public SessionStateBehavior SessionBehavior { get; private set; }          
            public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
            {
                SessionBehavior = sessioBehavior;
            }
    }

2. 覆盖

public class SessionControllerFactory : DefaultControllerFactory
{       
        protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
                return SessionStateBehavior.Default;

            var actionName = requestContext.RouteData.Values["action"].ToString();
            Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
            // [Line1]
            var cntMethods = controllerType.GetMethods()
                   .Where(m => 
                    m.Name == actionName &&
                    (  (  typeOfRequest == typeof(HttpPostAttribute) && 
                          m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
                       )
                       ||
                       (  typeOfRequest == typeof(HttpGetAttribute) &&
                          m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
                       )
                    )
                );
            MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
            if (actionMethodInfo != null)
            {
                var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
                                    .OfType<ActionSessionStateAttribute>()
                                    .FirstOrDefault();

                if (sessionStateAttr != null)
                {
                    return sessionStateAttr.Behavior;
                }
            }
            return base.GetControllerSessionBehavior(requestContext, controllerType);
 }

3. 在 Global.asax 中注册类

public class MvcApplication : System.Web.HttpApplication
 {
        protected void Application_Start()
        {
            // --- other code ---
            ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
        }
}

1
如果您想要用于ApiControllers呢? - achecopar
1
ApiControllers永远不会获取Session,因此没有必要。 - Daniel Lorenz

7
尝试从其他域名来提供图像,例如images.mysite.com。
这将为您带来两个好处:一是会话由cookie跟踪,因此images.mysite.com不会有cookie。二是它将为您提供额外的两个并发请求来检索图像。
您是否考虑设置一个 HttpHandler 来提供您的图像?

那是一个有趣的想法。我可以全面禁用该应用程序/站点的会话状态。但我仍然想知道每个请求/每个操作的事情是否也可能。 - Matt Sherman

5

如果您使用mvc3,SessionState属性非常有用。如何在mvc2中实现这一点需要更多的编码。

想法是告诉asp.net特定请求不会使用会话对象。

因此,为特定请求创建自定义路由处理程序。

public class CustomRouteHandler : IRouteHandler
    {
        public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
            return new MvcHandler(requestContext);
        }
    }

SessionStateBehavior 枚举有4个成员,您应该使用“disabled”或“readonly”模式来获取异步行为。

创建这个自定义路由处理程序后,请确保您的特定请求经过此处理程序。这可以通过在 Global.asax 中定义新路由来实现。

routes.Add("Default", new Route(
                "{controller}/{action}",
               new RouteValueDictionary(new { controller = "Home", action = "Index"}),
               new CustomRouteHandler()
                ));

添加此路由可以使所有请求都由您的自定义路由处理程序类处理。您可以通过定义不同的路由使其具体化。


1
你的解决方案适用于MVC 3+,你能分享一下如何在MVC 2中实现吗?因为你写道它可以通过更少的编码在MVC 2中实现。 - adardesign
如果对任何人有价值的话,我想说这个方法在MVC 5.2.3.0版本中仍然适用。但是,我必须将SessionStateBehaviour更改为Disabled并将路由添加到RouteConfig.cs(方法RegisterRoutes(RouteCollection))中,而不是在Global.asax中。 - MeterLongCat

3

将DefaultControllerFactory更改为自定义的ControllerFactory类。默认Controller.TempDataProvider使用SessionStateTempDataProvider,您可以更改它。

1.设置web.config/system.web/sessionState:mode="Off"。

2.创建DictionaryTempDataProvider类。

  public class DictionaryTempDataProvider : ITempDataProvider
  {
    public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
    {
      return new Dictionary<string, object>();
    }

    public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {
    }
  }

3. 创建 DictionaryTempDataControllerFactory

  public class DictionaryTempDataControllerFactory : DefaultControllerFactory
  {
    public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
      var controller = base.CreateController(requestContext, controllerName) as Controller;
      if (controller!=null)
        controller.TempDataProvider = new DictionaryTempDataProvider();

      return controller;
    }
  }

4. 在 global.asax.cs 的 Application_Start 事件中设置 DictionaryTempDataControllerFactory。

protected void Application_Start()
{
  RegisterRoutes(RouteTable.Routes);

  ControllerBuilder.Current.SetControllerFactory(
   new DictionaryTempDataControllerFactory()
  );
}

1
创建新控制器
使用 [SessionState(SessionStateBehavior.Disabled)] 装饰该控制器
重构代码,使您想要禁用会话状态的部分适用于该控制器

1
在我们的服务器上,IIS甚至不知道会话的存在 - 它是ASP.NET堆栈一次处理一个请求的。静态文件,如图像,永远不会受到影响。
你的ASP.NET应用程序是否可能正在提供文件而不是IIS?

没错,是ASP强制执行会话/同步的内容,而不是IIS本身。解决方案是像womp上面描述的那样在管道中更早地进行处理。 - Matt Sherman

0
我想分享我的解决方案,用于在特定请求(在我的情况下是WCF服务)中禁用ASP.NET会话,使用一个HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostMapRequestHandler += OnPostMapRequestHandler;
    }

    private void OnPostMapRequestHandler(object sender, EventArgs e)
    {
        var context = (sender as HttpApplication).Context;
        DisableSessionForSomeRequests(context);
    }

    private void DisableSessionForSomeRequests(HttpContext context)
    {
        if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
        {
            context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
        }
    }

    public void Dispose()
    { }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接