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

ASP.NET Web API中使用SessionService类出现空引用异常问题

问题分析与解决方案

咱们先拆解你遇到的两个核心问题:Web API中Session为空导致的空引用异常,以及普通控制器和Web API中SessionService使用方式的差异。

为什么会出现NullReferenceException?

你看到的异常触发在SetSessionValue方法里,根源是HttpContext.Current.Session为null。这是因为Web API默认是无状态设计,默认没有启用Session支持,而普通MVC控制器在框架初始化阶段就已经开启了Session,所以能正常访问。

为什么普通控制器和Web API的使用方式不同?

你在普通控制器里直接用SessionService.UnreadMessageCount没问题,是因为普通MVC请求上下文里Session已经被初始化;而Web API默认不加载Session,所以你只能通过SessionService.Get(单例实例)去访问,但这个单例模式本身就有严重问题——它是全局共享的,多个用户的Session数据会互相覆盖,而且依赖HttpContext.Current这种线程绑定的对象,在异步请求场景下极不稳定。

解决方案分两步走

第一步:启用Web API的Session支持

如果你的项目是基于OWIN的ASP.NET Web API,需要在Startup.cs中配置Session:

  1. 在ConfigureServices方法中添加Session服务:
public void ConfigureServices(IServiceCollection services)
{
    // 其他服务配置...
    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(30); // 设置Session超时时间
        options.Cookie.HttpOnly = true; // 增强安全性,防止前端JS访问
        options.Cookie.IsEssential = true; // 标记为必要Cookie,符合GDPR要求
    });
    services.AddHttpContextAccessor(); // 用于注入HttpContext
}
  1. 在Configure方法中启用Session(要放在UseMvc之前):
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 中间件配置...
    app.UseSession(); // 启用Session
    app.UseMvc();
}
  1. 给你的Web API控制器标记需要Session:
[SessionState(SessionStateBehavior.Required)]
public class YourApiController : ApiController
{
    // 控制器代码...
}

第二步:重构SessionService,去掉单例模式

你的SessionService用静态单例是错误的,因为Session是每个用户请求独立的,单例会导致用户数据串用。改成依赖注入的方式,让每个请求拥有独立的实例:

  1. 修改SessionService,去掉静态Get属性,通过构造函数注入IHttpContextAccessor:
public class SessionService : ISessionService
{
    private readonly HttpContext _httpContext;

    public SessionService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContext = httpContextAccessor.HttpContext ?? 
            throw new ArgumentNullException(nameof(httpContextAccessor));
    }

    public int UnreadMessagesCount
    {
        get { return GetSessionInt("UnreadMessagesCount"); }
        private set { SetSessionValue("UnreadMessagesCount", value); }
    }

    private int GetSessionInt(string key)
    {
        var sessionValue = _httpContext.Session.GetString(key);
        if (int.TryParse(sessionValue, out int count))
        {
            return count;
        }
        return 0;
    }

    private void SetSessionValue(string key, object value)
    {
        _httpContext.Session.SetString(key, value.ToString());
    }

    public void SetUserConversationMessages(OrganisationMembership organisationMembership)
    {
        // 替换成你的LINQ查询逻辑
        this.UnreadMessagesCount = organisationMembership.Messages.Count(m => !m.IsRead);
    }
}
  1. 在Startup.cs中注册ISessionService为Scoped(每个请求一个实例):
services.AddScoped<ISessionService, SessionService>();
  1. 在Web API控制器中通过构造函数注入ISessionService:
public class YourApiController : ApiController
{
    private readonly ISessionService _sessionService;
    private readonly IOrganisationService _organisationService;
    private readonly int _organisationId; // 假设你从用户上下文获取organisationId

    public YourApiController(ISessionService sessionService, IOrganisationService organisationService)
    {
        _sessionService = sessionService;
        _organisationService = organisationService;
        // 这里建议从用户身份信息中获取organisationId,而不是硬编码
        _organisationId = /* 获取当前用户的organisationId逻辑 */;
    }

    [HttpGet]
    [Route("MessageCount")]
    public IHttpActionResult GetMessageCount() // 方法名建议改成GetMessageCount,去掉重复的Count
    {
        try
        {
            OrganisationMembership organisationMembership = _organisationService.Find(_organisationId);
            if (organisationMembership == null)
            {
                return NotFound("Organisation membership not found");
            }
            _sessionService.SetUserConversationMessages(organisationMembership);
            return Ok(_sessionService.UnreadMessagesCount.ToString());
        }
        catch (Exception ex)
        {
            return InternalServerError(ex);
        }
    }
}

额外提示

  • 尽量避免在Web API中使用Session,因为Web API的设计初衷是无状态的,更推荐使用JWT令牌来保存用户状态信息。
  • 不要依赖HttpContext.Current,它是线程绑定的,在异步请求中可能会获取到错误的上下文,用IHttpContextAccessor是更安全的方式。

内容的提问来源于stack exchange,提问作者bthn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:21:17