我有一个用 ASP.NET Core 3.1编写的项目。
我需要将数据设置为单例服务中的会话:
_session.SetString("some key", "some value");
我从 DI 注入了会话对象:
public OperatorService(ILogger<OperatorService> logger,
ISession session,
IOptions<AppSettings> options)
{
this._session = session;
this._logger = logger;
this._appSettings = options.Value;
}
我调用 my 方法如下:
public void ChangeOperatorStatus(StatusChangeRequest request)
{
try
{
_session.SetString(request.Key, request.Value);
}
catch (Exception ex)
{
_logger.LogInformation($"Exception while changing status: {ex}");
}
}
但我得到以下例外:
IFeatureCollection has been disposed.rnObject name: 'Collection'.
我在 Startup.cs 的 ConfigureServices 方法中添加了一些代码:
services.AddHttpContextAccessor();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.HttpOnly = true;
})
.AddDistributedMemoryCache();
我app.UseSession();
添加到 Start.cs 的配置方法中。
我尝试了services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
,并从httpContextAccessor.HttpContext.Session
那里获得了会话,但遇到了相同的错误。
请帮助我,谢谢。
HttpContext 是单个请求的上下文。它提供对该单个请求的请求、响应属性等的访问。您无法缓存它,一旦请求结束,它就会失效。
会话是另一个短暂的东西 - 它只存在单个用户会话的时间。Web 应用的每个用户至少有一个会话。在单一实例中缓存其中一个会话可确保
- 引用将在一段时间后变得无效,当会话过期并且
- 单例将仅使用该用户的值,而忽略其他所有人的值。这本身就是一个错误,也是入侵应用程序的好方法。
- 如果管理员登录,会话对象可能会在接下来的 20、30 或 60 分钟内将管理员的设置应用于所有人。
这就是为什么使用会话对于每个请求的中间件而不是单一实例服务有意义。
正确使用 HttpContext
会话只能通过请求的上下文访问,因此获取正确的会话意味着获取正确的 HttpContext。正确的方法在David Fowler的 ASP.NET 核心指南中进行了解释:
❌ 坏 此示例将 HttpContext 存储在字段中,然后尝试稍后使用它。
private readonly HttpContext _context;
public MyType(IHttpContextAccessor accessor)
{
_context = accessor.HttpContext;
}
public void CheckAdmin()
{
if (!_context.User.IsInRole("admin"))
{
throw new UnauthorizedAccessException("The current user isn't an admin");
}
}
✅ 好 此示例将 IHttpContextAccesor 本身存储在字段中,并在正确的时间使用 HttpContext 字段(检查 null(。
private readonly IHttpContextAccessor _accessor;
public MyType(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public void CheckAdmin()
{
var context = _accessor.HttpContext;
if (context != null && !context.User.IsInRole("admin"))
{
throw new UnauthorizedAccessException("The current user isn't an admin");
}
}
请改用作用域内服务
由于单例无法知道要使用哪个会话。一种选择是简单地将该服务转换为作用域服务。在 ASP.NET Core 中,请求定义了一个范围。这就是控制器操作和管道中间件访问每个请求的正确 HttpContext 的方式。
假设服务由操作或中间件使用,也许唯一需要的更改是将AddSingleton<ThatService>
替换为AddScoped<ThatService>
扭转局面,或控制反转
另一种选择是,该单一实例的调用方应向其提供会话。而不是使用缓存的会话,例如:
public void SetStatus(string status)
{
_session.SetString(SessionKeys.UserStatus, "some value");
}
请求会话或 HttpContext 作为参数:
public void SetStatus(string status,ISession session)
{
session.SetString(SessionKeys.UserStatus, "some value");
}
并让呼叫者将正确的会话传递给它
我花了一段时间才解决这个问题。 就我而言,它是一个 3.1 aspnetcore,直到我从
public async void OnPost
自
public async Task<IActionResult> OnPost
看起来 HttpContext 在使用之前就被处理掉了......