【原文地址】About Technical Debates (and ASP.NET Web Forms and ASP.NET MVC debates in particular)
【原文发表日期】 Sunday, January 24, 2010 4:08 PM
【除了写博客外,我现在还使用Twitter发短贴和共享链接。请通过twitter.com/scottgu跟随我。】
技术争论在博客和twitter里无休止地进行着,这些争论涵盖每个开发人员社区。每个语言,框架,工具,和平台在某个特定的时间都不可避免地会至少有几个争论在进行中。
下面是我多年来对技术争论所做的几个总的观察,以及对一些我最近看到的,尤其是关于ASP.NET Web Forms 和 ASP.NET MVC的最新讨论的一些评论。
关于技术争论的总的观察
下面是几个总的观察,无关任何具体技术争论:
a) 开发人员喜欢充满热情地争论和比较语言,框架,APIs,和工具。每个编程社区(.NET, Java, PHP, C++, Ruby, Python等等)都如此。我认为你可以2种方式来看待这类宗教性的技术争论:
- 这些争论有时很讨厌,经常是浪费时间。
- 这些争论经常是一个既健康又活跃的社区的一个标志(因为激情意味着争论双方的人都深切地关注着,远远好过了无兴趣(apathy))。
就个人而言,我认为两者都对。
b) 开发什么东西从没有唯一的“正确之道(one right way)”。作为面试的开场题目,我有时会要求参试者以他们所能的效率最高的方式来对一组数字进行排序,大多数人都做的不是很好。这通常并不是因为他们不知道排序算法,而是因为他们从来不想起来询问其后的场景和需求,而这些对理解效率最高的方式的做法是至关紧要的。数字序列有多大?典型数字序列的随机度有多高?(是否大部分已经排好序了?数字差幅有多大?数字都是独特的么?重复数字是否群集在一起?)计算机架构的平行度有多高?作为排序的一部分,能否分配内存还是内存必须是常量? 等等。这些都是该问的重要问题,因为一组数字的效率最高和最优的排序方式依赖于对这些答案的理解。
任何时候人们声称某个编程问题只有一个唯一的“正确之道”时,他们几乎总是假定了一套固定的需求/场景/输入,而这对每个场景或每个开发人员来说,很少是最佳的。众所周知,编程中的大多数问题比将一组数字进行排序要复杂多了。
c) 优秀的开发人员使用差的工具/框架也能造出优秀应用来,差的开发人员使用优秀的工具/框架也能造出差的应用来。在根据所用的工具/框架对你正建造的应用的质量来做太广的假定(无论好坏)时要千万谨慎。
d) 开发人员(好的和差的)可以通过延伸自己,学习新的思路和方式方法来变得更加强大。即时他们最终并不直接使用新的东西,学习这个行动本身就能以积极的方式使得他们变得锐利。
e) 在技术行业里,变化是永恒的。变化可以令人害怕。但你是否被变化压倒(get overwhelmed),归根到底取决于你是否让你自己被压倒。别太担心需要停下来,突然学一堆新东西,你很少需要那么做。 避免被压倒的最好方法就是务实,对大范围的东西在高的层次上保持相当地了解(而不仅仅是技术和工具,也包括方法学),有自信认识到,如果学一门新技术很重要,那么你现有的开发技能的绝大部分能够转移过去并且有所帮助。不管怎样,对开发而言,句法和APIs很少是最重要的东西,问题的解决,客户共鸣和互动(customer empathy/engagement),以及能够专注一个项目并且训练有素的能力,更为宝贵。
f) 下面是我偶尔会给我的开发团队的人员一些在与他人协作和交流时的引导:
- 告诉别人他们很蠢,你很少会赢得争论,无论你对他们智商问题的解释是多么有善意,或者是多么有说服力。
- 总有某个人,在世界上某个地方,比你更聪明,- 别总是假设他们跟你不在一个屋里。
- 你交流的人往往会忘记你给予他们的赞誉,往往会记住以前的侮辱,- 所以说话时一定要审慎,否则后患无穷(come back to haunt you later)。
- 人们可以改变主意,也会改变主意, - 所以在争论中一定不要固执己见,他们改变主意的话,也别洋洋得意或者歧视他们。
g) 当我听人埋怨编程抽象不太好时, 我总是发现有点啼笑皆非,特别是当这些埋怨是通过博客来发表的时候,想想博客内容是使用HTML来显示的,用CSS来做的样式,用JavaScript来做交互的,在线上是用HTTP传输的,在服务器端是用高级语言编写的应用实现的,使用了面向对象的,垃圾回收的框架,是在解释的或JIT编译的字节码运行时之上运行的,博客内容和评论最终是保存在关系数据库中的,最终还是通过SQL查询字符串来访问的。所有的这些都是在主机服务器上的VM中运行的,而VM中的OS以kernel模式和用户模式进程界限对内存进行分配,使用线程调度工作,使用信号触发设备事件,使用抽象的存储API做硬盘持久。等下一次你读到 “ORM与存储过程之比较” 或者 “服务器控件,是好是坏?” 贴子的时候,非常值得把所有这些东西在脑子里再回想一番。而更有趣的争论都是关于特定问题的最佳抽象的。
h) 编程争论的历史是一个漫长的无限循环,其实大多数的编程想法都早已被解决过很多次了。不管是真是假,我们今天争论的许多问题很久以前就已在LISP 和 Smalltalk中解决了。令人啼笑皆非的是,尽管非常优雅地开创了许多东西,这二门语言却用得不太多了,琢磨去吧。
针对ASP.NET Web Forms / ASP.NET MVC之争的一些评论:
下面是对我最近看到在社区里传播的一些争论的几个评论,这些争论是关于ASP.NET Web Forms 和 ASP.NET MVC哪个方案最好的:
a) Web Forms 和 MVC是用来建造ASP.NET应用的2种方案,它们都是不错的选择。取决于应用的需求和参与开发的团队成员的背景,对特定的问题,每个方案都可以成为“最佳选择”。你可以使用两者中的任意一个建造出优秀应用来。你也可以使用两者中的任意一个建造出糟糕应用来。你是好的还是差的开发人员,并不取决于你选择了什么。使用两者,你可以是绝对地棒,也可以是毫无用处。
b) ASP.NET 和 Visual Studio开发团队在Web Forms 和 MVC上都投下了大量资源,随便哪个都不会消失。两者在接下来的几个月内都会有重大发布。ASP.NET 4包含了对Web Forms的重大更新 (干净的 ClientID 和 基于CSS的标识输出,较小的ViewState, URL导向, 新的数据和报表控件, 新的动态数据特性,新的SEO APIs, 新的VS设计器和项目改进等等)。ASP.NET 4中还会同时发布ASP.NET MVC 2,其中包含了重大的更新(强类型的辅助方法,模型验证,多区域,更好的脚手架支持,异步支持,更多的辅助方法APIs等)。别担心其中一个会变成死路一条或者你必须转向某一个。我怀疑,在我们大家都死了很久以后,在Internet某个地方还会有服务器依然还在运行基于 ASP.NET Web Forms 和 ASP.NET MVC两者的应用。
c) Web Forms 和 MVC 间共享的代码/基础设施/APIs 远远超过了参与争论双方的任意一位所提到的,- 认证,授权,成员,角色,URL导向,缓存,会话状态,用户信息,配置,编译,.aspx网页, .master 文件, .ascx 文件, Global.asax, 请求/回复/Cookie APIs, 健康监测,进程模型,跟踪,部署,AJAX, 等等等等。无论你是怎么构建你的UI的,你学到的所有常用的东西还是同样有效的。在未来,我们将继续投入大量资源建造可用于Web Forms 和 MVC两者的核心ASP.NET特性( 象URL导向,部署,输出缓存,和我们加到 ASP.NET 4 中的DataAnnotation的验证特性)。
d) 我经常发现围绕着编程模型之合适性和抽象的争论有点可笑。Web Forms 和 MVC两者都是编程web框架抽象,是建立在更广的框架抽象之上的,以高级编程语言编程,在运行引擎抽象之上运行,而运行引擎抽象本身又是在名为OS的巨大抽象之上运行的。你用Web Forms 和 MVC创建的是 HTML/CSS/JavaScript (所有的抽象都被持久为文本,在HTTP之上传输,而HTTP则是另一个高层次的协议抽象)。
该争论的有趣的问题不是这些抽象是好还是坏,而应该是哪个抽象你感觉最自然,哪个抽象与你项目的需要/场景/开发人员最匹配。
e) 我们即将对www.asp.net网站做一个非常重大的更新。作为更新的一部分,我们将发表更多的全程(end to end)教程/内容(Web Forms和MVC两者都有)。我们还将提供教程和指引,帮助开发人员很快地评估Web Forms和MVC两种方案,轻松地学习两者的工作原理基础,很快地决定哪个他们感觉最好。这将方便新的ASP.NET开发人员,以及已经知晓Web Forms或者MVC的开发人员,来理解和评估这2种方案,然后决定他们想要使用哪个方案。
f) 决定某个项目你究竟想使用Web Forms还是MVC,然后对此决定你要感觉高兴。两者都是好的选择。尊重别人做的选择,他们做的选择希望是一个好的,并且进展会顺利的选择。记住,十有八九,他们对他们自己的业务/技能的了解要比你所了解的多得多。同样地,希望你对你自己的业务/技能的了解也比他们所了解的多得多。
g) 与他人共享想法和最佳实践, 那是博客,论坛,邮件列单和社区的一个重大部分。它们之所以成功,是当人们知道他们的想法不会被批得体无完肤,而且他们会受到尊重的对待。请有建设性,而不是刻薄。请教授,而不是教训。记住,三人行,必有我师。
希望本文对你有所帮助,
Scott
【原文地址】ASP.NET MVC 2: Strongly Typed Html Helpers
【原文发表日期】 Sunday, January 10, 2010 8:57 PM
【除了写博客外,我现在还使用Twitter发短贴和共享链接。请通过twitter.com/scottgu跟随我。】
这是我针对即将发布的ASP.NET MVC 2所撰写的贴子系列的第一篇,这个博客贴子将讨论 ASP.NET MVC 2中新加的强类型HTML辅助方法。
现有的HTML辅助方法
ASP.NET MVC 1中发布了一套HTML辅助方法,可以用来在视图模板中帮助生成HTML界面。例如,要输出一个文本框,你可以在你的.aspx视图模板中使用Html.TextBox()辅助方法编写下列代码:
上面辅助方法的第一个参数提供了文本框的名称及id,第二个参数指定了它该有的值,然后上面的辅助方法会显示象下面这样的HTML到浏览器:
新的强类型HTML辅助方法
大家对ASP.NET MVC 2要求的一个常用特性是,要我们支持强类型的HTML辅助方法,这样的辅助方法使用 lambda 表达式来引用传到视图模板中的模型或视图模型。这可以促成更好的编译时视图检查(可以在编译时发现缺陷,而不是在运行时),还可以促成视图模板中更好的代码intellisense支持。
新的强类型HTML辅助方法现在已经内置于ASP.NET MVC 2中了,这些方法使用"Html.HelperNameFor()”的命名规范。例如,Html.TextBoxFor(), Html.CheckBoxFor(), Html.TextAreaFor()等等。它们支持使用lambda表达式来指定元素的名称和id,以及要显示的值。
例如,除了上面的Html.TextBox()辅助方法外,使用ASP.NET MVC 2,我们现在还可以使用新的Html.TextBoxFor()辅助方法:
注意上面,我们不再需要指定 “ProductName” 字符串参数,lambda表达式是相当灵活的,除了值以外,我们还可以获取我们模型对象中的属性/字段的名称。
因为这些HTML辅助方法是强类型的,编写lambda表达式时我们还可以在Visual Studio中得到完整的intellisense支持:
显示的HTML跟前面的后期绑定的HTML辅助方法版本的输出是一样的:
内置于ASP.NET MVC 2中的强类型HTML辅助方法列表
ASP.NET MVC 2对下列强类型的HTML辅助方法提供内置支持:
HTML元素辅助方法:
- Html.TextBoxFor()
- Html.TextAreaFor()
- Html.DropDownListFor()
- Html.CheckboxFor()
- Html.RadioButtonFor()
- Html.ListBoxFor()
- Html.PasswordFor()
- Html.HiddenFor()
- Html.LabelFor()
其他辅助方法:
- Html.EditorFor()
- Html.DisplayFor()
- Html.DisplayTextFor()
- Html.ValidationMessageFor()
我会在本系列的后期贴子中讨论ASP.NET MVC 2中改进了的“自动脚手架(auto-scaffold)"功能时,对新的Html.EditorFor() 和 Html.DisplayFor()辅助方法做进一步介绍。在本系列的下一个博客贴子中讨论ASP.NET MVC 2中改进了的验证支持时,我们还将使用Html.ValidationMessageFor()辅助方法。
Scaffolding中的强类型HTML辅助方法
VS 2008 和 VS 2010两者在用“添加视图”命令“生成(scaffolding)”新的强类型视图模板时,现在都会默认使用新的强类型HTML辅助方法。
例如,假设我们有一个象下面这样的简单 “ProductsController” 类,有一个“Edit” action方法,会为“Product”模型类显示一个编辑表单:
我们可以使用Visual Studio在Edit action方法中右击,选择“添加视图”上下文菜单命令来创建一个视图模板,我们将选择创建一个“Edit”模板,该模板是使用Product对象来生成的(scaffolded):
在ASP.NET MVC 2中,默认生成的视图模板现在使用了新的强类型HTML辅助方法来引用Product模型对象:
结语
包含在ASP.NET MVC 2中的强类型HTML辅助方法提供了一个很好的方式来在视图模板中得到更好的类型安全。这促成了对你的视图的更好的编译时检查(允许你在编译时,而不是运行时发现错误),还在Visual Studio中编辑视图模板时支持更丰富的intellisense。
希望本文对你有所帮助,
Scott
【原文地址】ASP.NET MVC 1.0 Release Candidate Now Available
【原文发表日期】 Tuesday, January 27, 2009 12:13 PM
今天,我们发布了ASP.NET MVC 1.0 最终版的候选版本(简称RC)。 点击这里下载(注:该链接才上线,所以如果链接不工作的话,请等几分钟,让你访问的服务器有机会更新)。该版本可以在Visual Studio 2008和免费的Visual Web Developer 2008下工作。
今天的ASP.NET MVC RC版本是我们在发布最终的1.0版本前的最后一个公开版本。我们预计在下个月(【译注】,指2月份)发布ASP.NET MVC 1.0 的最终版。
除了缺陷修补外,今天的版本还包含了若干个新的功能。它还包含了基于客户反馈对现有功能的几个改善。请阅读随ASP.NET MVC下载一起发布的发布说明,以了解所有改动的完整细节。该发布说明也包含了如何把用ASP.NET MVC Beta版本建造的现有应用升级到RC版的详细指令。
Visual Studio工具方面的改进
RC版本包含了Visual Studio工具方面的几个新功能(是在beta版本提供的新功能之外的,在这里我就不讨论beta中的这些功能了),这些功能包括:
添加控制器的命令
你现在可以在ASP.NET MVC项目中间键入Ctrl-M, Ctrl-C,或者右击 /Controller 文件夹,然后选择“Add->Controller(添加控制器)”上下文菜单项来创建新的控制器类:
这会调出“添加控制器”对话框,允许你给要创建的控制器取名,以及表示你是否想自动地生成(scaffold)常见的CRUD方法:
点击“添加”按钮会生成一个控制器类,并将它加到项目中去:
添加视图的命令
你现在可以在一个控制器的action方法中键入Ctrl-M, Ctrl-V,或者在一个action方法中右击,选择“添加视图”上下文菜单项来生成新的视图模板:
这会调出“添加视图”对话框,允许你给要创建的新视图取名(其中预先填充了基于约定的选项)。它允许你创建“Empty(空白)”的视图模板,或者根据控制器的action方法传给视图的对象类型自动地生成或scaffold视图模板。脚手架(scaffolding)基础设施在生成视图模板时会使用反射,这样它可以基于传给它的任何POCO (plain old CLR object,普通的CLR对象)生成新的模板。它对任何特别的ORM或数据实现都没有依赖性。
例如,在下面,我们表示想要基于从上面的action方法中传入的Product对象序列生成一个“列表”视图模板:
点击“添加”按钮会在 \Views\Products\ 文件夹中生成一个视图模板,内含默认的“脚手架”实现:
然后,我们运行应用,并在浏览器中请求 /products URL,就会看到取出的产品列表:
RC版本发布了好几个内置的脚手架模板:“Empty(空白)”, “List(列表)”,“Details(细节)”,“Edit(编辑)”和“Create(创建)”(你也可以添加自己的脚手架模板,稍后会提供更多细节)。
例如,要支持产品编辑,我们可以象下面这样在Products控制器上实现“Edit” action方法的HTTP-GET版本,然后调用“添加视图”命令:
在“添加视图”对话框中,我们可以表示我们将把一个Product对象传给我们的视图,然后选择“Edit”模板选项来生成脚手架实现:
点击“添加”按钮会在 \Views\Products\ 文件夹中生成一个编辑视图模板,内含默认的脚手架实现:
然后,我们可以运行应用,在浏览器中请求/products/edit/1 URL,来编辑Product细节:
要保存编辑后的改动,我们可以在Products控制器中实现“Edit” action方法的HTTP-POST版本:
注意在上面的代码中,在出错的情形下(例如,有人给一个数字型的值输入了一个非法的字符串),我们是如何重新显示视图的。“Edit(编辑)”和“Create(创建)”脚手架模板包含了在这种情形发生时,为保留用户输入和用红字标志不合法的输入元素所需的HTML验证辅助方法:
你最终大概不会原封不动地使用脚手架生成的模板,经常会完全换掉这些模板。但能够得到一个初始的实现并能很快地运行起来,针对你的场景有一个你可以轻松细调的起始视图模板是非常有用的。
因为脚手架基础设施支持针对任何普通的CLR对象生成脚手架视图,你既可以将其用于领域模型对象(包括用LINQ to SQL, LINQ to Entities, nHibernate, LLBLGen Pro, SubSonic, 和其他流行的ORM实现映射的对象),也可以用于自定义的Presentation Model/ViewModel类。
添加和定制脚手架模板
ASP.NET MVC的脚手架基础设施是用Visual Studio内置的T4模板架构(Scott Hanselman 在这里有一篇关于T4的好博客)来实现的。
你可以定制/改动任何内置的ASP.NET MVC脚手架模板实现。你还可以创建另外的脚手架模板(例如,ScottGu Crazy Look脚手架选项),让它们在“添加视图”对话框中作为选项显示出来。
要在整个机器层次定制/添加脚手架模板,打开 “C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC\CodeTemplates” 文件夹:
AddController 子文件夹包含了“添加控制器”对话框中的脚手架模板,而“AddView”子文件夹则包含了“添加视图”对话框中的脚手架模板:
填充在“添加视图”对话框中的脚手架模板只是带“.tt”文件扩展名的文本文件而已,这些“.tt” 文本文件包含了在模板被选中时会运行的行内C# 或 VB代码:
你可以打开和编辑任何现有的文件来定制默认的脚手架行为,你还可以添加新的“.tt”模板文件 -- 就象我在上面加的“Scott Crazy Look.tt”文件一样。在添加新的模板文件之后,“添加视图”对话框会更新,自动将其包括在可使用的脚手架选项列表之中:
除了在机器层次定制/添加模板文件外,你还可以在个别项目层次添加/改动这些模板文件,这还允许你将这些模板签入源码控制系统,在整个团队间轻松地使用它们。
你可以通过在项目下面加一个CodeTemplates文件夹,来在项目层次定制脚手架模板,然后你可以在其中建立“AddController”和“AddView”子文件夹:
你只要在项目中加一个同名的“.tt”文件,就可以覆盖任何一个默认的机器层次的模板文件。例如,在上面,我们覆盖了用在“添加控制器”场景中的默认“Controller.tt” 脚手架模板。
你可以将新的视图脚手架模板文件置于“AddView”文件夹中即可加入到列表中。例如,在上面,我们在项目中加了一个“Yet Another Crazy Look.tt” 视图模板。在使用“添加视图”对话框时,我们现在就会看到定义在机器和项目层次的所有模板的集合:
注: 当你将 “.tt”模板加到 \CodeTemplates 文件夹中时,确认在属性表格中将每个“.tt”模板文件的“Custom Tool(定制工具)”属性设成了空白字符串(否则的话,在运行时你会得到错误),你也许还需要关闭和重新打开项目以清除错误列表中会出现的一个伪错误。不久,我们将发表更多讨论创建/定制脚手架模板的博客贴子。
转到控制器/转到视图命令
RC版现在支持在项目中的控制器和视图间的快速导航。
当你的鼠标在控制器的action方法中时,你可以键入Ctrl-M, Ctrl-G,来快速地导航到对应的视图模板。你也可以通过在当前的action方法中右击,选择“Go To View(转到视图)”菜单选项才实现同样的导航跳跃:
在上面的例子中,我们在ProductsController类的“Edit” action方法中用了“Go To View”命令,这会在VS中打开\Views\Products\Edit.aspx视图模板,并使其拥有默认的焦点:
在视图模板中,你现在还可以键入 Ctrl-M, Ctrl-G 来快速地导航到视图对应的控制器类中。你也可以在视图模板中右击,选择“Go To Controller(转到控制器)”菜单选项来实现同样的导航操作:
编译视图的MSBuild任务
在默认情形下,当你编译一个ASP.NET MVC项目时,它会编译项目中除了视图模板文件中代码以外的所有代码。在ASP.NET MVC Beta版本中,如果你想要编译视图模板中的代码的话,你需要编写自己的MSBuild任务。ASP.NET MVC RC 版本则包含了一个内置的MSBuild 任务,你可以用它来把视图包括成为项目编译过程的一部分。该任务会核实应用中所有视图,母板页,部分视图的语法和其中的行内代码,如果遇上问题的话,会给你编译错误。
但因为性能的缘故,我们不建议在开发期间运行它来做快速编译,但将它加到特定的编译配置(例如,staging和部署)或者在与 Build 或 CI (连续集成)服务器一起使用时还是非常方便的。 请参阅发布说明中有关启用该任务的详细步骤。
重构视图的支持
在你使用VS 2008的“Rename(重新命名)”重构命令对控制器类或action方法重新命名时,\Views子文件夹中的文件和文件夹名现在会自动更新。VS 2008 会在控制器类被更新时,将标准的基于约定的命名模式施用于现有的视图文件/文件夹。
视图方面的改进
RC版包括了若干个特定于视图的改进,这些改进是基于预览版的反馈融合进来的。
不带后台代码文件的视图
基于反馈,我们把默认的视图模板改成了不再带后台代码文件,这个变动有助于强化视图在MVC应用中的目的(视图意在纯粹的显示,不该包含任何与显示无关的代码),去掉了项目中没被使用的文件(对大多数人来说)。
RC版本现在添加了对从使用泛型的基类继承视图模板的C#和VB句法支持。例如,在下面,我们在Edit.aspx视图模板中使用了这个句法,它的 “inherits”属性是从ViewPage<Product> 类型继承而来的:
不使用后台代码文件的一个好处是,在将视图模板文件加到项目后,你现在能立刻得到intellisense。在以前的版本中,你需要在生成视图后马上编译一次才能在其中得到intellisense。RC版本使得添加和立刻编辑视图文件的流程不再需要编译,过程也更加流畅。
重要主要事项: 如果你在把用以前版本创建的ASP.NET MVC项目升级到RC版,请确认遵循发布说明中的步骤,即,\Views文件夹中的web.config文件中的一些设置需要更新一下,上面的基于泛型的句法才会工作。
Model属性
在以前的ASP.NET MVC版本中,你使用ViewData.Model属性来访问传给视图的强类型模型对象:
上面的句法依然工作,虽然现在你还可以使用ViewPage上一个顶层的"Model"属性:
这个属性的功能跟前面的代码例子完全一样,它主要的好处在于,它允许你编写简明的代码。它还允许在你想要视图模板只与传给它的强类型模型打交道的情形下,避免使用ViewData字典。
设置标题
加到新的ASP.NET MVC项目的默认母板页模板在<head>部分拥有一个<asp:contentplaceholder/>元素,这可方便视图模板来控制要显示的HTML页面中的 <title> 元素,而不需要控制器明确地传递一个“title” 参数来配置它(这个方法是以前ASP.NET MVC版本中的默认方法,从责任的角度来看,我们认为是有问题的)。
(【译注】该功能也引入了一个缺陷,详情请参考Phil Haack的博客 Controls Collection Cannot Be Modified Issue with ASP.NET MVC RC1,你需要下载RC的更新版,还请参阅的Phil Haack的博客 ASP.NET MVC RC Refresh)
例如,为定制Edit视图的<title>来包括当前产品的名字,我们现在可以加下面的代码到Edit.aspx模板中,直接根据传进视图的模型对象生成标题:
然后,上面的代码就会使得浏览器在运行时使用产品名显示标题:
除了设置<title>元素外,你还可以使用上面的方法在运行时动态地添加其它<head>元素。这会非常有用的另一个常见场景是,为搜索引擎优化(SEO)配置特定于模型/视图的<meta/>元素。
强类型的HTML/AJAX辅助类
有一个不少人都提出的要求是,在使用视图的HTML和AJAX辅助对象时,在指称Model时使用强类型表达式的句法(而不是字符串)的能力。
在ASP.NET MVC Beta版本中,这是不可能的,因为HtmlHelper和AjaxHelper辅助类并没有在它们的签名中呈示模型的类型,所以大家需要建造直接基于ViewPage<TModel>基类的辅助方法才能达成这个目的。
ASP.NET MVC RC 版本引进了新的HtmlHelper<TModel>和 AjaxHelper<TModel> 类型,是在ViewPage<TModel> 基类上呈示的。这些类型现在允许任何人建造使用了表达式句法的强类型HTML和AJAX辅助扩展来指称View的模型。 例如:
核心ASP.NET MVC V1程序集中的HTML表单辅助扩展方法还将使用不是基于表达式的字符串句法,今天发布的MVC Futures程序集(可在RC版下工作)有几个基于表达式句法的表单辅助方法的初始实现,我们计划对这些方法多迭代几次,然后考虑在下一个版本中把它们加到ASP.NET MVC核心程序集中。
当然,你还是可以添加自己的辅助方法(使用基于字符串或强类型的表达式方法)。内置的HTML/AJAX 辅助方法都是可以去掉的(因为它们是扩展方法),如果你要用自己的版本来替换或覆盖它们的话。
表单提交方面的改进
RC版本包括了若干个特定于表单提交方面的改进:
在常见场景下不再要求[Bind(Prefix=””)]
RC版本不再要求你明确地使用[Bind]特性(或将其前缀值设置为"")才能将进来的没有前缀的表单提交参数映射到复杂的action方法参数上了。
要理解其意义,让我们来实现ProductsController的 “Create” 场景。我们先来实现“Create” action方法的HTTP-GET 版本,我们用下面的代码来返回一个基于空白的Product对象的视图:
然后我们在action方法中右击,选择“添加视图”命令,生成基于一个Product对象的“Create” 视图模板的框架:
注意上面我们的Html.TextBox()辅助方法是如何引用产品对象上的“ProductName”和“SupplierID”属性的。这会生成象下面这样的HTML标识,其中的input “name” 属性是 “ProductName” 和 “SupplierID”:
然后,我们可以实现“Create” action方法的HTTP-POST 版本,该方法接受一个Product对象作为参数:
在 ASP.NET MVC Beta版本中,我们需要在上面的Product参数前加一个[Bind(Prefix=””)]特性,否则的话,ASP.NET MVC绑定基础设施只会寻找带“productToCreate.”前缀的表单提交值(例如,productToCreate.ProductName 和 productToCreate.SupplierID),而不会找到我们表单的提交值(因为这些值没有对应的前缀)。
在RC版本中,默认的action方法绑定器开始还会尝试将productToCreate.ProductName表单值映射到Product对象上。但如果它们找不到这样的值的话,它们现在会尝试将“ProductName” 映射到Product对象上。这使得你需要传递复杂对象到action方法的场景的句法更加干净和简明。你可以在映射领域对象(象上面的Product对象)以及Presentation Model/ViewModel类(象ProductViewModel 类)时利用这个功能。
我们的Create action方法的一个完整的实现(包括基本的输入类型错误处理)会象这样:
现在,我们的Create action方法会保存Product对象,如果所有的值都输入正确的话。在用户尝试使用不合法的产品属性值创建Product时,例如,用“Bogus” 字符串来替换一个合法的Decimal值,表单就会重新显示,用红字标记非法的输入元素:
ModelBinder API方面的改进
ASP.NET MVC RC版中的模型绑定基础设施已经重构过了,加了额外的扩展点,允许自定义的绑定和验证定义。你可以阅读ASP.NET MVC RC的发布说明以了解这些细节。
除了注册为类外,现在的模型绑定器还能注册为接口。
IDataErrorInfo支持
ASP.NET MVC 中默认的模型绑定器现在支持实现了IDataErrorInfo接口的类。这允许以一种共同的方式来抛出验证错误信息,该方式可在Windows Forms, WPF ,以及现在的 ASP.NET MVC应用中公用。
单元测试方面的改进
ASP.NET MVC RC版本包括了单元测试方面一些显著的改进:
ControllerContext不再从RequestContext继承而来
RC版本包括了对 ControllerContext类的重构,该重构极大地简化了常见的单元测试场景。ControllerContext不再从RequestContext继承而来,而是在其中封装了RequestContext,将其呈现为一个属性。ControllerContext的属性和继承类现在都是virtual的,而不是sealed的了,这极大地方便了mock对象的创建。
要理解其有助之处,让我们来考虑一个象下面这样的action方法,其中使用了内置的“Request” 和 “User” 对象:
在以前的ASP.NET MVC版本中测试上面的action方法要求模拟RequestContext和 ControllerContext(还在RouteData对象中引进了一些不是显而易见的构造器)。
在RC版中,我们现在可以象下面这样单元测试(使用了Moq框架来模拟我们Controller的ControllerContext,允许我们模拟Request.IsAuthenticated 和 User.Identity.Name属性):
这样的重构改进不光有助于测试控制器的action方法,还有助于测试过滤器,路径,自定义的actionresult类型,已经其它各种场景。
AccountsController的单元测试
包括在RC版中的ASP.NET MVC项目模板现在添加了25个预制的单元测试,这些测试是用来核实AccountsController类的行为的(该类是在默认情形下加到项目中,用来处理登录和帐号管理场景的)。这方便了重构和更改AccountsController。AccountsController的实现也被修改过了,以利于基于非成员提供器的权限系统的集成。
防范跨站请求伪造攻击(Cross Site Request Forgery-简称CSRF)
跨站请求伪造攻击(也称为XSRF攻击)会导致使用了信任的浏览器代理的用户在一个网站上采取非计划中的行动。这些攻击依赖于用户也许还登录了另外一个网站这样一个事实,一个恶意的网站可以通过创建一个发向起始网站的请求(例如,用黑客网站上的<img src=””/>来链向起始网站的URL)来做攻击。该请求是用用户的浏览器做的,用了用户的认证凭据。攻击者希望用户的认证或会话cookie还是有效的,如果是这样的话,攻击者有时可以采取破坏性的行动。你可以在这里了解该黑客技术的细节。
ASP.NET MVC RC现在包括了一些内置的防范CSRF攻击的辅助方法,可以有助于减轻CSRF攻击。例如,你现在可以使用Html.AntiForgeryToken()辅助方法,在表单中生成一个隐藏的input记号:
这个辅助方法会发出一个HTTP cookie,在我们的表单中生成一个隐藏的输入元素。恶意的网站不能同时获取这2个值。
然后我们可以在我们想要保护的任何action方法上施加一个新的 [ValidateAntiForgeryToken]特性:
这会检查合适的记号的存在,如果不匹配的话,会阻止我们的HTTP-POST action方法的运行(减小了成功的CSRF攻击的机会)。
File处理方面的改进
ASP.NET MVC RC版包含了若干个文件处理方面的改进:
FileResult 和 File() 辅助方法
RC版添加了一个新的FileResult类,可被用来表示一个文件将作为一个控制器action方法的ActionResult而返回。 Controller基类现在还包含了一套File()方法,可以方便创建和返回FileResult。
例如,让我们假设我们想要建造一个相片管理网站,我们可以象下面这样定义一个简单的Photo类,在其中封装了要保存的相片的细节:
然后,我们可以象下面这样使用新的File()辅助方法,在PhotoManager控制器中实现一个DisplayPhoto action方法,用来显示数据库中的相片。在下面的代码中,我们把要显示的字节,以及文件的 mime类型传给了File()辅助方法,如果我们将 <img src=””/> 元素指向我们的action方法的话,浏览器就会在页面中显示对应的相片:
假如我们想要终端用户能够下载相片,并保存在本地的话,我们可以象下面这样实现一个DownloadPhoto action方法。在下面的代码中,我们传入了第三个参数,该参数将导致ASP.NET MVC设置一个头信息,使得浏览器显示一个“保存为。。”的对话框,在其中预先填充我们提供的文件名:
当用户点击指向/PhotoManager/DowloadPhoto/1232 URL的链接时,会提示保存是否要相片:
文件上传的支持
RC版中还包括了对上传文件以及多部分mime内容的内置的模型绑定器支持。
例如,我们可以有这样一个<form>,其enctype属性被设置为“multipart/form-data”,会向/PhotoManager/UploadPhoto URL提交表单。如果表单中有一个<input type=”file” name=”fileToUpload”/>元素,可由终端用户选择一个文件,这个文件会作为HttpPostedFileBase对象传给我们的action方法:
然后我们可以使用HttpPostedFileBase对象来访问上传文件的原始字节,它的mime类型,以及将其保存到数据库中或硬盘上。
AJAX方面的改进
The ASP.NET MVC RC 版包括了若干个AJAX方面的改进:
jQuery Intellisense 文件包括在ASP.NET MVC项目模板中了
新创建的 ASP.NET MVC 项目现在包括了标准的jQuery库文件(同时包括完整的和压缩的版本),以及vsdoc intellisense文档文件,该文件会被Visual Studio用来提供更丰富的intellisense支持(你可以在这里了解有关详情):
这可以在客户端代码块和JavaScript文件中促成丰富的jQuery JavaScript intellisense:
今天的RC版本发布的是jQuery 1.2.6,我们计划在 ASP.NET MVC 1.0 最终版中发布即将发行的jQuery 1.3.1,同时会包括一个相应的JavaScript intellisense文件的更新版。
Request.IsAjaxRequest Property属性
Request.IsAjaxRequest属性可以用来检测某个请求是否来自客户端的AJAX调用 (这在AJAX功能没有启用,你需要优雅地降级的场景下会非常有用)。在RC版本中,这个方法中的逻辑已被更改为可以识别“X-Requested-With” 这个HTTP头信息(除了由ASP.NET AJAX发送的表单域值外)。这是个众所周知的头信息,为诸多JavaScript库(象Prototype, jQuery, 和 Dojo)所发送,这促成了一种统一的方式来在ASP.NET MVC请求中检测是否是AJAX调用。
JavaScriptResult ActionResult 和 JavaScript() 辅助方法
Controller这个基类现在含有一个JavaScript()辅助方法,它返回一个新的ActionResult,其类型为JavaScriptResult。这支持了这样的功能: 从服务器端返回原始的JavaScript脚本,之后为内置的ASP.NET MVC辅助方法在客户端运行。这在你想要根据服务器端的逻辑在客户端执行条件性的JavaScript的情形下会非常有用。
结语
我们非常兴奋,我们处在了ASP.NET MVC V1版本的最后冲刺阶段。请尽快地报告你在RC版中发现的任何问题,这样我们就可以在最终版中解决这些问题。开发团队计划在下几个星期内仔细地监听任何反馈,假定没有任何大的问题的话,将在下个月发布正式的V1版本。
希望本文对你有所帮助,
Scott