ASP.NET Core过滤器向Action传对象及购物车为空问题排查
OnActionExecuted购物车对象为空的问题 让我们一步步拆解你遇到的问题,帮你定位原因并解决:
1. 先看核心矛盾:过滤器私有字段_mand为什么会空?
你的过滤器在OnActionExecuting中给_mand赋值,但到了OnActionExecuted就变成空,大概率和过滤器生命周期或对象关联逻辑有关,我们逐个排查:
原因1:过滤器被注册为单例,实例复用导致并发问题
如果你的WinkelmandSessionFilter是按Singleton(单例)注册的,多个请求会共享同一个过滤器实例。当请求并发时,可能出现某个请求的OnActionExecuted在另一个请求的OnActionExecuting之前执行,这时候_mand还没被赋值,自然是空的。
解决方法:
把过滤器注册为Scoped,确保每个请求都有独立的过滤器实例:
// 在Program.cs/Startup.cs中注册 builder.Services.AddScoped<WinkelmandSessionFilter>();
原因2:Action参数名称不匹配,模型绑定创建了新对象
你在过滤器里把购物车存入ActionArguments["cart"],但你的Action方法参数是Winkelmand mand——ASP.NET Core的模型绑定会自动创建一个新的Winkelmand实例,完全不会用到过滤器传入的cart对象。这就导致:
- Action里修改的是全新的
mand,过滤器的_mand还是原来的旧对象 - 极端情况下,如果模型绑定失败,还可能间接导致
_mand赋值流程中断
解决方法:
要么把Action参数名改成cart,让模型绑定复用过滤器传入的对象:
[ServiceFilter(typeof(WinkelmandSessionFilter))] public IActionResult BonEdit(Winkelmand cart, NieuwViewModel model) { var bon = new Bon(); // ... 你的业务代码 cart.bonToevoegen(bon); // 这里用cart代替原来的mand bon.genereerPdf(); return RedirectToAction(nameof(BonVoorbeeld), bon); }
要么修改过滤器里的ActionArguments键名,和参数名mand保持一致:
public override void OnActionExecuting(ActionExecutingContext context) { _mand = ReadCartFromSession(context.HttpContext); context.ActionArguments["mand"] = _mand; // 键名和参数名统一 base.OnActionExecuting(context); }
原因3:Winkelmand类序列化/反序列化失败
如果Winkelmand无法被Json.NET正确序列化/反序列化,ReadCartFromSession会抛出异常,导致OnActionExecuting没完成_mand的赋值,到OnActionExecuted时_mand自然是空的。
检查点:
Winkelmand必须有无参构造函数(Json.NET默认要求)- 需要序列化的属性必须是公共的,或者配置了Json.NET可访问私有成员
- 类中不能有循环引用(如果有,给循环引用的属性加
[JsonIgnore])
验证方法:
给ReadCartFromSession加异常捕获,看看是不是序列化出了问题:
private Winkelmand ReadCartFromSession(HttpContext context) { try { var cartJson = context.Session.GetString("cart"); return string.IsNullOrEmpty(cartJson) ? new Winkelmand() : JsonConvert.DeserializeObject<Winkelmand>(cartJson); } catch (Exception ex) { // 这里可以打日志,查看序列化失败的具体原因 return new Winkelmand(); } }
原因4:Session根本没启用
如果你的应用没正确配置Session,调用context.Session会直接抛出异常,导致OnActionExecuting中断,_mand没机会被赋值。
检查点:
确保在Program.cs中正确配置了Session,且UseSession()的位置正确:
// 注册Session服务 builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); // ... // 启用Session,必须放在UseRouting和UseEndpoints之间 app.UseSession(); app.UseRouting(); // ... app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
额外优化建议
为了彻底避免依赖过滤器私有字段的问题,你可以直接在OnActionExecuted中从Action参数获取修改后的购物车:
public override void OnActionExecuted(ActionExecutedContext context) { // 从Action参数里拿修改后的购物车 if (context.ActionArguments.TryGetValue("mand", out var cartObj) && cartObj is Winkelmand cart) { WriteCartToSession(cart, context.HttpContext); } base.OnActionExecuted(context); }
这样不管过滤器的私有字段状态如何,都能正确拿到最新的购物车对象。
内容的提问来源于stack exchange,提问作者TimelordNeill

