ScottGu 在其 最新的博文 中推荐了 Simone Chiaretta 的文章 13 ASP.NET MVC extensibility points you have to know,该文章为我们简单介绍了 ASP.NET MVC 中的 13 个扩展点。Keyvan Nayyeri(与Simone合著了 Beginning ASP.NET MVC 1.0 一书)又陆续发表了一些文章,对这13个扩展点分别进行深入的讨论。我将在以后的随笔中对这些文章逐一进行翻译,希望能对大家有所帮助。
ASP.NET MVC 设计的主要原则之一是可扩展性。处理管线(processing pipeline)上的所有(或大多数)东西都是可替换的。因此,如果您不喜欢ASP.NET MVC 所使用的约定(或缺乏某些约定),您可以创建自己的服务来支持您的约定,并将其注入到主管线中。
在本文中,我们将从管线开始直到视图呈现,逐一向您展示每个ASP.NET MVC开发者都必须了解13个扩展点。
1.RouteConstraint
通常情况下你可以使用正则表达式对 url 参数进行约束,但如果您的约束不仅仅取决于单一参数,您可以实现 IRouteConstrains 的方法,并在其中添加你的验证逻辑。
比如对日期的验证,url中可能会包含年、月、日,而你需要验证这三者是否可以组合成一个有效的日期。
2.RouteHandler
RouteHandler 是在路由选择之后进行处理的组件,它并不仅仅针对ASP.NET MVC。显然,如果您改变了RouteHandler,那么对请求的处理将不再使用ASP.NET MVC,但这在您使用其他HttpHandler或 经典的WebForm 进行路由处理时却是非常有用的。
3.ControllerFactory
ControllerFactory 是基于路由的组件,它选择正确的 controller 并对其实例化。default factory 会查找实现了 IController 并且以 Controller 结尾的类,然后通过反射使用无参构造函数进行实例化。
但如果您希望使用依赖注入,就不能再使用 default factory,而必须使用支持 IoC 的 controller factory。MvcContrib 和 Ninject Controller Factory 都包含支持 IoC 容器的controller factory。
4.ActionInvoker
ActionInvoker 顾名思义是负责调用(invoke)action的。默认的 action invoker 通过方法名、action 名或其他可能的 selector attribute 来查找 action,然后调用 action 方法以及定义的filter,最终执行得到action result。
你会发现大部分执行管线存在于 ControllerActionInvoker 类的逻辑之中。因此,如果希望改变这些约定,如 action 方法的选择逻辑、http 参数映射到 action 参数的方式、选择和执行filter的方式等,您需要扩展该类并重写需要修改的方法。
可以参阅 NinjectActionInvoker I developed to allow injection of dependencies inside filters。
5.ActionMethodSelectorAttribute
使用默认的 action invoker 时,action 的选择是基于名称的。您也可以实现自己的 Method Selector 以改善对于action的选择。在框架中已经包含了 AcceptVerbs 特性,它允许您指定使用哪一个 HTTP Verb 来处理action的响应。
例如,您也许会希望基于浏览器所支持的语言或浏览器类型(如移动设备的浏览器或桌面浏览器)来进行action的选取。
6.AuthorizationFilter
这种过滤器是在 action 执行之前执行的,用来确保请求是有效的。
框架中已经包含了一些autorization过滤器,最有名的莫过于 Authorize 特性,它用来检查当前用户是否允许执行该action。另一个是 用来阻止CSRF攻击的ValidateAntiForgeryToken。如果您希望实现自己的authorization,那么必须实现接口。例如,日期中的小时。
7.ActionFilter
Action Filters在action执行前后执行。OutputCache过滤器是几个核心过滤器之一。这可能是您最有可能使用的扩展点,并且在我看来,controller只关心它的主要工作,而view所需要的所有其他数据都必须从action过滤器内部获取,这样的实现对于一个组织良好的view来说,是十分关键的。
8.ModelBinder
默认的 model binder 使用参数名称进行 HTTP 参数到 action 方法参数的映射。例如,http 参数 user.address.city 将映射到方法参数user的Address属性的City属性。DefaultModelBinder 也同样适用于数组和其他列表类型。
更进一步来说,例如,您可能希望从数据库中进行检索,直接根据 person 的 id 将其转换为 Person 对象。Timothy Khouri(网名SingingEels)在他的文章 Model Binders in ASP.NET MVC 中更好的阐述了这种方法。他的代码基于 Preview 5,但其理念是一样的。
9.ControllerBase
所有的 Controller 均继承自基类 Controller。要想在 action 中封装自己的逻辑和约定,创建自己的父类使所有 Controller 继承自该类,是一种很好的方式。
10.ResultFilter
与 ActionFilter 类似,ResultFilters 在 ActionResult 前后执行。OutputCache 过滤器也可以作为 ResultFilter 的示例。另外,比较常用的诠释这种过滤器的示例是日志记录。如果您希望在页面返回给用户时记录日志,可以编写自定义的 RenderFilter,在 ActionResult 执行之后记录日志。
11.ActionResult
ASP.NET MVC 提供了很多 result 用来呈现视图、JSON、纯文本、文件并重定向到其他 action。如果您需要其他类型的 result,可以自定义 ActionResult,并实现 ExecuteResult方法。例如,如果您希望将 PDF 文件作为结果发送,您需要使用 PDF 库编写能够生成 PDF 的 ActionResult。又如RSS feed,可参见 how to write a RssResult in this post。
12.ViewEngine
您可能不需要编写自己的 view engine,但您也许可以考虑使用其他引擎来替代默认的 WebForm view engine。在我看来,最有趣的引擎就是 Spark。
如果您确实希望编写自己的 view engine,可以看一下 Brad Wilson 的文章: Partial Rendering & View Engines in ASP.NET MVC。
13.HtmlHelper
视图必须十分简单整洁,它们只能包含 html 标记并调用 HtmlHelper 的辅助方法。视图中不能包含任何代码,所以辅助方法必须十分方便,使您可以将代码从视图中提取出来,放到一个可测试的环境中去。正如 Rob Conery 所说:如果有if,就构造辅助方法(If there‘s an IF, make a Helper)。
什么是 HtmlHelper 辅助方法?其实就是 HtmlHelper 类的扩展方法,这是唯一的要求。
你可以从Rob的文章 Avoiding Tag Soup 中了解到为什么说 HtmlHelper 是封装视图中代码的好方法。
在您的应用中该使用哪个呢?
正如您所猜测的那样,并不是所有的应用都需要扩展以上的 13 个扩展点。最可能在所有应用中进行扩展的是 ActionFilter 和 HtmlHelper。另外,您很可能会使用其他人编写的扩展,如使用了 IoC 容器的 ControllerFactory 或用来摆脱 WebForm 的 ViewEngine。
但是,学习这些扩展点并进行尝试是十分重要的,这样您才会做出选择,并随时准备在必要的时候使用这些强大的扩展点。下周我将发表一些文章来阐述如何使用这些扩展点。
如果您想详细了解更多关于该话题的内容,可以考虑购买即将出版的 Beginning ASP.NET MVC(我是作者之一)或 Professional ASP.NET MVC(ASP.NET MVC开发团队编写)或 ASP.NET MVC in Action (Jeffrey Palermo和Ben Scheirman著)。
我是否遗漏了某些您认为重要的扩展点呢?您是否使用过我上面提到的扩展点呢?我很想听听您所遇到的场景。