在开始本章节之前,因为微软已经释放了MVC2的Beta版本,作者已经将自己的项目升级成了Beta2的版本(删除了Default.aspx并修正了一些Bug)来运行本程序。不过在本章中为了衔接上一章节的内容,继续开始MVC2(Preview2)的内容。至于Beta2和正式版的新增内容将在MVC的最后于大家阐述。
一、神奇的URL:
或许你在运行本项目的时候发现本项目没有任何起始页,但是在浏览器启动(或者是输入"http://localhost:端口号"的时候就已经进入了首页。这个在以往的程序中是无法做到的(因为以往的程序中URL地址中的页面必须在物理上有映射逻辑,比如:http://localhost/Default.aspx,意味着一定在根目录下存在着一个Default.aspx页面);然而在MVC中却并不如此。如果你尝试做一个实验,请输入以下地址:
http://localhost:端口号/FinanceManager/AddItem
您将会发现——页面神奇地跳转到到了AddItem页。但是按照原来的路径映射,您应该认为说“FinanceManager下根本没有任何页面,只是一个Controller而已!”或许你还会按照原思路作出这样的想法:http://localhost:端口号/Views/AddItem.aspx,但是遗憾的是,这种想法也是错误的。哦,MyGod……
其实这种URL路径已经和真实的文件映射已经没有很大的关系了。你仔细看过我的前面一篇文章对于该项目的介绍之后,再了解结构之后你应该清楚地知道:所有的视图页面都在Views文件夹中,而Controllers中只有一个控制器文件。并且你有心的话,会发觉你那个实验的URL中的FinanceManager恰好是该Controller中的文件名,而AddItem恰恰又是该Controllers中的一个函数名称;打开Views下文件夹你细细观察,其中就有一个文件夹叫做FinanceManager,而且还包含着一个被成为“AddItem”的页面……
啊?MVC那么神奇?竟然可以通过地址映射“自动寻找”对应的逻辑控制代码,而且返回Views中的某个页面?是的,您的猜想是对的。一般而言,一个MVC的URL的标准格式如下:
v 其中“控制器名”是位于Controllers文件夹下的一个文件“排除后缀”的名称,这就意味着您必须命名规范,Controller文件必须是“XXXXController”,而且是区分大小写的(不信?您可以试试: http://localhost:端口号/FinanceManager/addItem)。当输入这样一个URL以后,程序自动会搜索全部本工程根目录下(以及所有子目录)中带有Controller后缀名的文件,去除Controller并与“控制器名”进行完全匹配,直到找到(没有找到将报错,说页面找不到)。实际上,既然MVC会自动搜索,您当然大可不必死板地将Controller文件放到一个地方(比如示例程序的Controllers文件夹),但是便于管理,微软(包括我)也是推荐您这样做的。
v 在找到这样一个与之匹配的Controller之后,接下来读取“逻辑函数名”,逻辑函数实质就是这个Controller函数中的某个公共函数,因为也是同名匹配,所以您不能进行重载(设想:如果重载,系统到底取哪个函数呢?糊涂了),而且不允许使用泛型类型(比如您不能定义诸如:public ActionResult XXX<T>(T param){……}之类的函数,理由很简单——因为一旦定义了未知类型,您在使用这个函数的头一件事情必须要告诉该函数的类型,但是Controller是自动解析的,你根本没有办法告诉它什么类型,至于说为什么可以使用“系统基本类型?”的形式,比如int?(本质也是泛型,等同于Nullable<int>…,是因为int?只有两种类型——要么int,要么null,这两种都可以被识别的,而且最关键的在于Nullable<int>已经自行定义给出了)。当然还有其它的一些限制,因为考虑读者最容易犯忌的往往是这两个,所以特别着重提出。至于其它的可以在网络上自行搜索,自己动脑好好思考为什么。
由此可见,微软内部默认的机制已经规定了MVC中URL的映射处理的方式:从冒号后开始解析,遇到第一个“/”前的一串字符串就是映射Controller后缀的文件名,待找到以后深入找里边的某个方法名(从第一个“/”到第二个“/”之间的一串字符串)。这样看来,“约定胜于配置”是微软MVC项目的关键之处。
二、自定义我的URL——我的地盘我做主:
或许你对这种默认的规则还怀有好奇心,想找找微软究竟定义在何处了:来,打开你的Global.aspx文件,看看里边是不是有这样一个静态方法的定义?
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default”,
"{controller}/{action}",
new { controller = "FinaceManager", action = "Index" }
);
}
你发现模板自行定义了一个{controller}/{action}的格式。一般说来,MVC2中Controller和Action必须定义(也就是说无论如何,系统总得要找到Controller和Action),只不过你可以指定默认值,就像上面给出的例子一样。因为给出了controller和action的默认值,因此你直接就可以http://localhost:[端口号],而不必指出具体的Controller和Action了。
实际上,URL完全可以被自定义,比如你可以这样指定一个URL:
routes.MapRoute("Default”,
"FinanceManager/{action}",
new { controller = "FinaceManager", action = "Index" }
);
}
你仔细观察我的这个示例代码,你将发现那个{controller}已经不复存在了。但“不复存在”不代表没有,因为很明显在new{…}已经定义。注意,“FinanceManager”是一个常数字符串,这意味着您在URL输入的时候必须是:http://localhost/:[端口号]/FinanceManager/才可以,不能说此处的FinanceManager就是Controller名。您完全可以使用{controller}和{action}指定URL的格式化,这就意味着您可以自定义格式,让系统知道什么地方是controller,什么地方是action,以便让系统自行解析。当然,在缺省{controller}或者{action}的任何一个(或者全部)的定义情况下,请务必指定默认值。总而言之,判断的标准是:按照您所指定的URL系统应该能够解析得到controller和action,否则必须指定默认值。
当然,除了可以定义常量(如上面的FinanceManager常量字符串前缀,变量也可以定义到URL中去,看这个例子:
routes.MapRoute("Default”,
"{controller}/{action}/{id}",
new { controller = "FinaceManager", action = "Index",id=”” }
);
}
这是微软标准的一个URL的定义(默认的,您下载我的代码看到的那个我已经去除了id,因为项目用不着)。您按照这个URL输入的标准输入应该是http://localhost:[端口号]/ControllerName/ActionName/Id,此时即便没有Id,因为Controller和Action已经可以找到,所以不会有错误,但是考虑到id作为参数传入对应Action方法会抛出异常(此可以参考我以后的“参数自动绑定”后续文章)。因此您最好指定一个默认值。