我看到很多人谈论在他们的 ASP.NET MVC 项目中使用基本控制器。我见过的典型示例用于日志记录或 CRUD 脚手架。基本控制器类还有哪些其他好的用途?
10 回答
基本控制器类没有很好的用途。
现在听我说完。
Asp.Net MVC,尤其是 MVC 3 有大量的可扩展性钩子,它们提供了一种更加解耦的方式来向所有控制器添加功能。由于您的控制器类对应用程序非常重要和核心,因此保持它们轻巧、敏捷并与其他所有东西松散耦合非常重要。
日志基础设施属于构造函数,应该通过 DI 框架注入。
CRUD 脚手架应由代码生成或自定义 ModelMetadata 提供程序处理。
全局异常处理应由自定义 ActionInvoker 处理。
全局视图数据和授权应由操作过滤器处理。使用 MVC3 中的全局操作过滤器更容易。
常量可以放在另一个名为 ApplicationConstants 的类/文件中。
基本控制器通常由没有经验的 MVC 开发人员使用,他们不了解 MVC 的所有不同可扩展性部分。现在不要误会我的意思,我不是在判断并与出于所有错误原因使用它们的人一起工作。它只是经验,为您提供了更多解决常见问题的工具。
我几乎可以肯定,除了基本控制器类之外,没有一个问题是您无法使用另一个可扩展性挂钩解决的。不要采用最紧密的耦合形式(继承),除非有重要的生产力原因并且您不违反 Liskov。我宁愿花不到 1 秒的时间在我的控制器上输入一个属性 20 次,public ILogger Logger { get; set; }
而不是引入一个以更重要的方式影响应用程序的紧密耦合。
甚至像 userId 或多租户密钥这样的东西也可以进入 ControllerFactory 而不是基本控制器。基本控制器类的耦合成本是不值得的。
我喜欢使用基本控制器进行授权。
我没有用“Authorize”属性装饰每个动作,而是在基本控制器中进行授权。从数据库中为登录用户获取授权操作列表。
请阅读以下链接以获取有关授权的更多信息。 在自定义控制器工厂中进行通用授权的良好做法?
我在我的许多项目中都使用了基本控制器,并且效果非常好。我主要用于
- 异常记录
- 通知(成功、错误、添加..)
- 调用 HTTP404 错误处理
我用它来访问会话、应用程序数据等。
我还有一个应用程序对象,它包含应用程序名称等内容,我从基类访问它
本质上,我将它用于我经常重复的事情
哦,我应该提到我不将它用于商业逻辑或数据库访问。我猜对于基类来说,常量也是一个不错的选择。
我所做的是使用通用控制器基类来处理:
- 我创建
BaseCRUDController<Key,Model>
了需要一个ICRUDService<TModel>
对象作为构造函数参数的对象,因此基类将处理Create / Edit / Delete。并确保在虚拟模式下处理自定义情况 ICRUDService<TModel>
具有Save / Update / Delete / Find / ResetChache /... 之类的方法,我为我创建的每个存储库实现它,以便我可以向它添加更多功能。- 使用这种结构,我可以添加一些通用功能,例如PagedList / AutoComplete / ResetCache / IncOrder&DecOrder(如果模型是 IOrderable)
错误/通知消息处理:布局中带有
@TempData["MHError"]
代码的部分和基本控制器中的属性,例如公共通知错误 { 设置 { TempData["MHError"] = value; } get { return (Notification) TempData.Peek("MHError"); } }
有了这个抽象类,我可以轻松处理每次必须编写或使用代码生成器创建的方法。但是这种方法也有它的弱点。
根据我的经验,您想要放入基本控制器的大部分逻辑理想情况下都将放入动作过滤器中。动作过滤器只能用常量初始化,所以在某些情况下你不能这样做。在某些情况下,您需要将操作应用于系统中的每个操作方法,在这种情况下,将您的逻辑放在基础中可能比使用新的 actionFilter 属性注释每个操作方法更有意义。
我还发现将引用服务的属性(否则它们与控制器分离)放入基础中很有帮助,使它们易于访问和一致地初始化。
我们将 BaseController 用于两件事:
- 应该应用于所有控制器的属性。
- Redirect 的覆盖,通过检查重定向 URL 是否为本地 URL 来防止开放重定向攻击。这样所有调用重定向的控制器都受到保护。
我现在使用基本控制器使用i18N库进行国际化。它提供了一种方法,我可以使用它来本地化控制器中的任何字符串。
过滤器不是线程安全的,在数据库访问和依赖注入的情况下,数据库连接可能在使用时被其他线程关闭。
我们使用了基本控制器:
- 覆盖该
.User
属性,因为我们使用自己的 User 对象,该对象应该具有我们自己的自定义属性。 - 添加全局
OnActionExecuted
逻辑并添加一些全局操作过滤器