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:
- 在
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 }
- 在
Configure方法中启用Session(要放在UseMvc之前):
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 中间件配置... app.UseSession(); // 启用Session app.UseMvc(); }
- 给你的Web API控制器标记需要Session:
[SessionState(SessionStateBehavior.Required)] public class YourApiController : ApiController { // 控制器代码... }
第二步:重构SessionService,去掉单例模式
你的SessionService用静态单例是错误的,因为Session是每个用户请求独立的,单例会导致用户数据串用。改成依赖注入的方式,让每个请求拥有独立的实例:
- 修改
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); } }
- 在
Startup.cs中注册ISessionService为Scoped(每个请求一个实例):
services.AddScoped<ISessionService, SessionService>();
- 在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
相关产品推荐
相关产品推荐

