对于ASPNET MVC,我们执行了以下操作:
- 默认情况下,
SessionStateBehavior.ReadOnly
通过覆盖设置所有控制器的操作DefaultControllerFactory
- 在需要写入会话状态的控制器操作上,标记属性以将其设置为
SessionStateBehavior.Required
创建自定义ControllerFactory并重写GetControllerSessionBehavior
。
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
var DefaultSessionStateBehaviour = SessionStateBehaviour.ReadOnly;
if (controllerType == null)
return DefaultSessionStateBehaviour;
var isRequireSessionWrite =
controllerType.GetCustomAttributes<AcquireSessionLock>(inherit: true).FirstOrDefault() != null;
if (isRequireSessionWrite)
return SessionStateBehavior.Required;
var actionName = requestContext.RouteData.Values["action"].ToString();
MethodInfo actionMethodInfo;
try
{
actionMethodInfo = controllerType.GetMethod(actionName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
catch (AmbiguousMatchException)
{
var httpRequestTypeAttr = GetHttpRequestTypeAttr(requestContext.HttpContext.Request.HttpMethod);
actionMethodInfo =
controllerType.GetMethods().FirstOrDefault(
mi => mi.Name.Equals(actionName, StringComparison.CurrentCultureIgnoreCase) && mi.GetCustomAttributes(httpRequestTypeAttr, false).Length > 0);
}
if (actionMethodInfo == null)
return DefaultSessionStateBehaviour;
isRequireSessionWrite = actionMethodInfo.GetCustomAttributes<AcquireSessionLock>(inherit: false).FirstOrDefault() != null;
return isRequireSessionWrite ? SessionStateBehavior.Required : DefaultSessionStateBehaviour;
}
private static Type GetHttpRequestTypeAttr(string httpMethod)
{
switch (httpMethod)
{
case "GET":
return typeof(HttpGetAttribute);
case "POST":
return typeof(HttpPostAttribute);
case "PUT":
return typeof(HttpPutAttribute);
case "DELETE":
return typeof(HttpDeleteAttribute);
case "HEAD":
return typeof(HttpHeadAttribute);
case "PATCH":
return typeof(HttpPatchAttribute);
case "OPTIONS":
return typeof(HttpOptionsAttribute);
}
throw new NotSupportedException("unable to determine http method");
}
AcquireSessionLockAttribute
[AttributeUsage(AttributeTargets.Method)]
public sealed class AcquireSessionLock : Attribute
{ }
将创建的控制器工厂连接到 global.asax.cs
ControllerBuilder.Current.SetControllerFactory(typeof(DefaultReadOnlySessionStateControllerFactory));
现在,我们可以在一个会话中同时拥有read-only
和read-write
会话状态Controller
。
public class TestController : Controller
{
[AcquireSessionLock]
public ActionResult WriteSession()
{
var timeNow = DateTimeOffset.UtcNow.ToString();
Session["key"] = timeNow;
return Json(timeNow, JsonRequestBehavior.AllowGet);
}
public ActionResult ReadSession()
{
var timeNow = Session["key"];
return Json(timeNow ?? "empty", JsonRequestBehavior.AllowGet);
}
}
注意:即使在只读模式下,ASPNET会话状态仍然可以写入,并且不会引发任何形式的异常(它只是不锁定以保证一致性),因此我们必须小心标记AcquireSessionLock
需要写入会话状态的控制器操作。