我建立使用ASP.NET MVC 4 Web应用程序,通过实体框架通过T-SQL数据库提供的数据存储。 我整合审计日志,因为我去了,我想提供行动的一个很好的人类可读的总结,这样我可以提出一个友好的日志中包含“用户Bob登录”,“用户Alice明确声明查看更新文章“富””,等等。
审计记录目前包括:
- GUID
- 时间戳
- 用户身份
- 动作类别(控制器名称)
- 动作(action方法名)
- ISERROR(布尔值;真方式,无论这是一个错误的记录,或者这个动作没有成功完成)
- 系列化细节BLOB
此刻,我的记录使用它实现了一个自定义属性IActionFIlter
; 在OnActionExecuting()
方法登录尝试的操作(连载之类的URL,参数等的详细BLOB)和OnActionExecuted()
方法返回,并设置ISERROR为true,如果没有错误,并追加或者返回的结果或异常使用错误消息和堆栈跟踪等的细节。 我想补充的描述字符串另一列,但我不能看到一个整洁的方式做到这一点。
我最远的是传递了一个字符串属性,像“登录用户$用户”,然后将日志方法扫描$字符的字符串,从参数字典,字典的键值匹配任何替换词字(减去$字符)。 这是一个小的限制; 例如,如果物品是通过ID编号存储,那么你就可以管理最好的是“用户编辑18第37条”。 有没有真正的方式来获得的用户名或文章的标题; 因为它是在编译时烤你不能实例数据传递给属性,而且我真的不希望我的测井方法是制作各种数据库的调用来获取这种类型的数据,这不仅是因为它则变得不可能(或至少一个真正的疼痛)有一个单独的通用测井方法。
所有这一切的选择是有一个静态的审计记录类并调用类似AuditRecord.WriteLog(foo);
所有的地方,也许有某种描述类的,我可以使用(或继承)来描述不同类型的操作,存储所有参数,并产生需要的描述字符串,但似乎不太优雅的给我; 我真的很喜欢能够只是标记[AuditLog]
在方法的顶部,并知道它会被记录下来。
我想避免的条件逻辑的巨额资金,就像使用控制器和动作名字在一些大的switch语句来选择正确的字符串模板。 如果我能只得到的东西像测井方法的文章标题保持那么它会被罚款。 是否有一个整洁,简单的方法来做到这一点?
我们最近有两个方面的审计记录历史和应用在我们新的MVC项目更复杂的安全规则,在工作中类似的讨论。
最终,我们想出了最“优雅”的解决办法是让控制器动作中的方法调用(你的另一种方法)。
例如:
[HttpPost]
public ActionResult CreateItem(Item item)
{
//Simplified
CheckSecurity(SecurityTypes.ItemCreation);
LogActivity("Created an item");
//Rest of action code
}
这给了我们的灵活性,考虑到所有可能的使用情况,并允许我们的逻辑绕成简单的使用方法,以减少重复的代码。
这可能是来不及回答,但我觉得这是一个很好的选择,以保持使用行为过滤器的属性,并能够访问每个请求的生命周期对象。
正如阿那克西曼德指出它上面,根本的问题是,属性是由CLR解决,所以他们的一生是无法控制的,他们不与IoC容器拌匀(让他们短暂的,每个请求的情况下,等)。
通常,在.NET 属性的新实例被创建的每个其被反射 ( 时间分辨 GetCustomAttribute
方法)。
此外, 在MVC /的WebAPI的情况下,操作筛选属性缓存 ,所以他们通常都创建了一次。
结论是,属性被设计成只编,换句话说,他们应该只包含元数据 (它们是DTO)。 不幸的是,我的理解是MVC和的WebAPI框架都不能以这种方式设计的。 要限制行为过滤器属性,简单的DTO,并能够管理他们周围的逻辑部分的生命周期,特殊的手段必须采取措施。
我觉得你的使用情况完全适合在史蒂芬面包车Deursen的提供的解决方案大文章 。 它说明如何从逻辑分开的属性数据,并且它是基于全局注册,即所谓的“调度员”,用作为依赖IoC容器的动作滤波器。 容器不是静态解析 。 它是在当它在应用程序初始化注册的全球过滤器的构造提供。 所以每次执行时,它看起来对正在执行的操作的任何属性标记,它解决了一个通用的接口,它的属性是通用的参数。 相反,具有合并数据和行为的行动筛选器属性的,你最终使用两个类:一个普通的老属性 - 标记 - 和它的逻辑对应的通用接口的相应的实现。 该容器是用来解决通用接口。 如果你的过滤器依赖于每个请求的组件,您可以创建你需要的服务的通用接口的实现。 如果它不依赖于其他服务,但需要每个请求的生命周期(测量开始,例如一个动作的结束之间的时间),它也没有工作,感谢对使用容器来解决通用接口。 上述文章中包含的WebAPI,MVC和ASP.NET 5代码示例。
此外,马克·西曼取得了一篇文章上同样的做法。
我认为它不适合所有的情况下提供了一个很好的解决方案,如授权过滤器,也许例外过滤器,但对我来说这是许多操作筛选最优雅的方式。
更好的办法是,当你查看它格式化这个数据,而不是在记录过程中建造这些东西。
如果操作为“登陆”,并记录用户可用(你应该这样做),那么你建立观众该消息。
所以,你记录所有的原始事件,然后构建“视图模式”或在此基础上的数据是更具描述性的“阅读模式”。 这可以允许,如果你想改变它的描述,你甚至重新解析所有的原始数据。 你可以记录大量的数据还未被使用,所以你可以在以后的描述中实现它。
IMO,洒方法这样里面的动作似乎并不像一个好主意,控制器或基本控制器上操作过滤器是清洁的。 如果你想这样做,你可以使用一个AOP(面向方面编程)框架,以避免交叉切割...