You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC:如何在尝试修改只读Session时抛出异常

ASP.NET MVC:如何在尝试修改只读Session时抛出异常

这个需求真的很实用——静默丢弃Session修改太容易留坑了,我之前排查bug的时候也吃过这个亏。下面给你分享两种可行的实现方案,你可以根据项目情况选择:

方案一:自定义Session包装类 + HttpModule全局拦截

这个方案的核心思路是包装原生的Session对象,在所有修改操作前检查当前Session是否处于只读状态,如果是就直接抛出异常。

步骤1:实现只读感知的Session包装类

我们继承HttpSessionStateWrapper,重写所有会修改Session的方法和索引器,在执行实际操作前先校验状态:

public class ReadOnlyAwareSessionStateWrapper : HttpSessionStateWrapper
{
    private readonly bool _isReadOnly;

    public ReadOnlyAwareSessionStateWrapper(HttpSessionState session, bool isReadOnly) 
        : base(session)
    {
        _isReadOnly = isReadOnly;
    }

    // 统一的只读校验方法
    private void ThrowIfReadOnly()
    {
        if (_isReadOnly)
        {
            throw new InvalidOperationException("当前Session为只读状态,禁止执行修改操作!");
        }
    }

    // 重写所有修改Session的方法
    public override void Add(string name, object value)
    {
        ThrowIfReadOnly();
        base.Add(name, value);
    }

    public override void Remove(string name)
    {
        ThrowIfReadOnly();
        base.Remove(name);
    }

    public override void Clear()
    {
        ThrowIfReadOnly();
        base.Clear();
    }

    public override void RemoveAll()
    {
        ThrowIfReadOnly();
        base.RemoveAll();
    }

    public override void RemoveAt(int index)
    {
        ThrowIfReadOnly();
        base.RemoveAt(index);
    }

    // 重写索引器的set方法
    public override object this[string name]
    {
        get => base[name];
        set
        {
            ThrowIfReadOnly();
            base[name] = value;
        }
    }

    public override object this[int index]
    {
        get => base[index];
        set
        {
            ThrowIfReadOnly();
            base[index] = value;
        }
    }
}

步骤2:实现HttpModule替换Session对象

通过IHttpModule在请求生命周期中,自动识别标记了ReadOnly SessionState的控制器,并替换其Session为我们的包装类:

public class ReadOnlySessionEnforcementModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        // 绑定AcquireRequestState事件,此时Session已可用
        context.AcquireRequestState += OnAcquireRequestState;
    }

    private void OnAcquireRequestState(object sender, EventArgs e)
    {
        var app = (HttpApplication)sender;
        var httpContext = app.Context;

        // 如果Session未初始化,直接跳过
        if (httpContext.Session == null) return;

        // 获取当前请求的控制器信息
        var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
        if (routeData?.Values["controller"] == null) return;

        var controllerName = routeData.Values["controller"].ToString();
        // 注意替换成你项目的控制器命名空间
        var controllerType = Type.GetType($"YourProjectNamespace.Controllers.{controllerName}Controller");
        if (controllerType == null) return;

        // 检查控制器是否标记了ReadOnly的SessionStateAttribute
        var sessionStateAttr = controllerType.GetCustomAttribute<SessionStateAttribute>();
        if (sessionStateAttr?.Behavior == SessionStateBehavior.ReadOnly)
        {
            // 替换原生Session为自定义包装类
            httpContext.Session = new ReadOnlyAwareSessionStateWrapper(httpContext.Session, true);
        }
    }

    public void Dispose()
    {
        // 无需额外清理,空实现即可
    }
}

步骤3:注册HttpModule

在web.config中注册我们的模块,根据你的IIS模式选择对应配置:

IIS集成模式(推荐)

<system.webServer>
  <modules>
    <add name="ReadOnlySessionEnforcementModule" type="YourProjectNamespace.ReadOnlySessionEnforcementModule" />
  </modules>
</system.webServer>

IIS经典模式

<system.web>
  <httpModules>
    <add name="ReadOnlySessionEnforcementModule" type="YourProjectNamespace.ReadOnlySessionEnforcementModule" />
  </httpModules>
</system.web>

方案二:使用ActionFilter局部拦截

如果不想全局生效,也可以用ActionFilter针对单个控制器或Action做拦截:

public class EnforceReadOnlySessionAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var sessionStateAttr = filterContext.Controller.GetType()
            .GetCustomAttribute<SessionStateAttribute>();
        
        if (sessionStateAttr?.Behavior == SessionStateBehavior.ReadOnly)
        {
            // 替换Session为包装类
            filterContext.HttpContext.Session = new ReadOnlyAwareSessionStateWrapper(
                filterContext.HttpContext.Session, true);
        }
        
        base.OnActionExecuting(filterContext);
    }
}

然后在目标控制器上添加这个过滤器:

[SessionState(SessionStateBehavior.ReadOnly)]
[EnforceReadOnlySession]
public class HomeController : Controller
{
    // ...
}

效果验证

现在只要在标记了ReadOnly的控制器里尝试修改Session,比如执行Session["Test"] = "Value";,就会立刻抛出InvalidOperationException,而不是静默丢弃修改,能帮你在开发阶段及时发现问题。

备注:内容来源于stack exchange,提问作者Virus721

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 08:53:04