**《ASP.NET MVC 5 编程实战》

========== ========== ==========
[作者] (美) Dino Esposito
[译者] (中) 潘丽臣
[出版] 清华大学出版社
[版次] 2015年03月 第1版
[印次] 2015年03月 第1次 印刷
[定价] 59.80元
========== ========== ==========

【前言】

Web Forms 的最常见应用场景是,你要开发专注于呈现数据并使用第三方控件套装的应用程序。

ASP.NET MVC 可用于处理其他所有方面,包括客户端单页面应用程序的框架搭建。

ASP.NET MVC 足以成为任何一个需要实体后台的应用程序的理想 Web 平台,对于那些以多设备实用功能为目标的 Web 应用程序来说尤其如此。

【第01章】

(P003)

在 ASP.NET MVC 中,每个请求的结果最终都会执行某个操作 —— 根本上来说也就是特定类上的方法。操作执行的结果会与一个视图模板一起传递给视图子系统。结果和模板随后会用于生成浏览器的最终响应。用户不需要将浏览器指向某个页面,他们只需要放置一个请求即可。

使用 ASP.NET MVC ,可以获得对标记的完全控制,并能随意用你最喜欢的 JavaScript 框架来套用样式和注入脚本代码。

(P004)

在软件中,术语 URI (统一资源标识符) 是指通过一个位置或一个名称来引用资源。

当 URI 通过位置识别资源时,它被称为 URL 或统一资源定位符。

当 URI 通过名称识别资源时,它就成为一个 URN 或统一资源名称。

(P005)

在 ASP.NET 中, HTTP 处理程序是一个实现了 IHttpHandler 接口的组件。

HTTP 处理程序可以解析出令牌中的 URL ,并使用该信息来标识要调用的类和方法。

(P007)

处理请求的专门组件是控制器。控制器是一个只有方法而无状态的类。

独特的系统级 HTTP 处理程序负责将传入的请求分派到一个专门的控制器类,这样该类的实例就会执行给定的操作方法并产生响应。

URL 路由 HTTP 模块通过查看 URL 并把它们分派到最适当的执行器来处理传入的请求。 URL 路由 HTTP 模块取代了旧版本 ASP.NET 的 URL 重写功能、在其核心处, URL 重写由挂接请求、解析原始 URL 和指示 HTTP 运行时环境组成,用以处理 “可能相关却不同” 的 URL 。

(P008)

URL 重写确实可以将处理该请求的物理网页与所需的 URL 进行解耦。

在 ASP.NET MVC 中, URL 路由提供了把传入的 URL 映射到控制器类和操作方法的服务。

URL 路由模块最初是作为 ASP.NET MVC 组件来开发的,而现在成为 ASP.NET 平台的一个原生部分,且如前所述,它能够同时为 ASP.NET MVC 和 ASP.NET Web Forms 应用程序提供服务,虽然是通过略微不同的 API 。

只有与预定义 URL 模式 (也称为路由) 相匹配的请求才能享有 ASP.NET MVC 运行时。所有这类请求会被路由到一个共同的 HTTP 处理程序,该处理程序将控制器类实例化并调用该类中一个定义了的方法。接下来,控制器方法会进而选择视图组件生成实际的响应。

(P009)

在 ASP.NET MVC 中实现一个纯粹的 REST 解决方案是可能的,但需要做一些额外工作。

(P010)

应用程序路由通常在 Global.asax 文件中注册,并在应用程序启动时得到处理。

(P011)

RegisterRoutes 是一个在通常命名为 APP_Start (但可以随意重命名该文件夹) 的单独文件夹中所定义的 RouteConfig 类的方法。

所支持的路由必须添加到由 ASP.NET MVC 管理的路由对象的静态集合中。该集合就是 RouteTable.Routes 。

(P012)

为确保按正确顺序处理该路由,必须将它们按照从最具体到最不具体的顺序罗列出来。

(P014)

默认情况下, ASP.NET 路由系统会忽略那些其 URL 可以映射到实际存在于服务器上的文件的请求。请注意,如果服务器文件存在,则路由系统将忽略该请求,即使请求匹配了路由。

(P015)

通过 URL 路由筛选器的任何请求都被映射到一个控制器类,通过执行类上的给定方法来进行服务。

常见的做法包括为应用程序所实现的每个重要功能配备一个控制器类。

(P017)

控制器类的编写可以概括为两个简单的步骤 : 第一,创建从 Controller 继承 (直接或间接均可) 而来的类;第二,添加一系列的公共方法。然而,必须阐明两个重要的细节 : 系统如何获知控制器类要实例化,以及它如何确定要调用的方法。

不论如何定义 URL 模式,任何请求都必须根据控制器名称和操作名称来解析。这是 ASP.NET MVC 的一个支柱。如果 URL 包含了一个 {controller} 占位符,那么控制器名称会自动从 URL 中读取。如果 URL 包含了 {action} 占位符,那么操作名称也将从 URL 中自动读取。

(P020)

将特定动词分配到给定的操作方法的能力很自然地会导致重复的方法名称。具有相同名称的两种方法在控制器类中都可以被接受,只要它们接受不同的 HTTP 动词。否则,将引发异常,因为 ASP.NET MVC 不知道如何解析一词多义的情况。

还可以使用多个单独的特性,每个特性用于一个 HTTP 动词。

(P021)

容器对象通常也称为视图模型,可以是一对 名称 / 值 的浅显字典,或者是特定的视图强类型类。

(P022)

控制器操作方法可以访问任何通过使用 HTTP 请求而提交的输入数据。输入数据可以从各种来源检索,包括表单数据、查询字符串、 Cookies 、路由值和提交文件。

在编写操作方法的主体时,当然可以访问传给 Request 对象及其子集的任何数据,这些 Request 对象及子集包括 Form 、 Cookies 、 ServerVariables 和 QueryString 。

在 ASP.NET 中, Request.Params 字典产生于四个不同字典的组合,即 QueryString 、 Form 、 Cookies 和 ServerVariables 。也可以使用 Request 对象的 Item 索引器属性,它会提供一样的功能,并按一下顺序在字典中搜索匹配项 : QueryString 、 Form 、 Cookies 和 ServerVariables 。

(P028)

ActionResult 类的最终目的是准备好要返回到浏览器的 HttpResponse 对象。

在 ASP.NET Web Forms 中,构成 HTML 的任务是通过页面完成的。开发人员创建 ASPX 页面作为视图模板和代码隐藏类的混合容器。抓取结果的操作与实际响应的产生,这两者在单一的运行时环境中是难以分开的。

在 ASP.NET MVC 中,产生出结果是操作方法的责任;管理响应的构成和服务则是框架的责任。而最后,构成 HTML 标记更是另一个系统组件 —— 视图引擎 —— 的责任。

视图引擎规定了视图模板的语法 (比如 ASPX 、 Razor 和 Spark) ;而由开发人员决定合并到视图的原始数据格式。

(P029)

视图模板文件的扩展名也取决于视图引擎的实现。对于 ASP.NET MVC 中的两个预定义的视图引擎来说,如果选择 ASPX 视图引擎,那么扩展名为 .aspx ;如果选择 Razor 视图引擎,则扩展名为 .cshtml (或 .vbhtml) 。

ASP.NET MVC 非常适合实现那些会从 Ajax 上下文的 jQuery 代码片段中回调的简单 Web 服务。只需设置一个或多个操作方法,以返回 JSON 字符串而不是 HTML 。

Json 帮助器方法可以获取一个普通的 .NET 对象,并使用内置的 JavaScriptSerializer 类将它序列化为字符串。

ASP.NET MVC 会将任何从操作方法返回的值 (数字、字符串或自定义对象) 封装成一个 ContentResult 对象。

ContentResult 对象的执行会导致浏览器中值的序列化。

空白返回值实际上是被映射到了 EmptyResult 对象,而它的执行不会触发任何操作。

(P030)

控制器的主要目的是服务于用户界面的需要。需要执行的任何服务器端函数都要映射到一个控制器方法并从用户界面触发。执行完自己的任务后,控制器方法会选择下一个视图、封装一些数据并指示数据的提交。

(P031)

请记住,在 ASP.NET MVC 中,解决方案建筑中的任何分层都取决于你。

【第02章】

(P033)

在 ASP.NET MVC 中,只需要处理两种主要类型的组件。一种是控制器,它负责执行请求并为原始输入生成原始结果。另一种是视图引擎,它负责生成基于由控制器计算出的结果的任何预期的 HTML 响应。

可以把 ASP.NET MVC 应用程序看作是能够服务于各种响应的组件集合,这些响应包括 HTML 、 JavaScript 、 JavaScript 对象标志 (JSON) 和纯文本。

(P034)

视图引擎是为浏览器实际生成 HTML 输出的组件。视图引擎负责为每个请求返回 HTML ,并且它通过将视图模板和由控制器传递进来的数据进行融合来准备其输出。该模板以一种引擎专用的标记语言来表示;其数据在字典或强类型对象中进行封装传递。

在 ASP.NET MVC 中,视图引擎只是一个实现固定接口 —— IViewEngine 接口的类。每个应用程序可以有一个或多个视图引擎。在 ASP.NET MVC 5 中,每个应用程序默认配备两个视图引擎。

(P035)

ViewEngines 类是跟踪当前已安装引擎的系统资源库。

(P036)

最可能面临使用 ViewEngines.Engines 的情况是当需要添加一个新的视图引擎或卸载现有视图引擎的时候。这需要在应用程序启动时进行,更准确地说,是在 Global.asax 里的 Application_Start 事件中。

(P037)

控制器和视图引擎的活动都由一个外部的管理器对象 (操作调用程序) 进行协调。

(P038)

控制器类上的 View 方法会将几段数据一起封装进一个单独的容器 : ViewResult 类中。

视图的名称是控制器操作上 View 方法应该提供的参数之一。如果程序员没有显式定义这样的参数,则系统会按照惯例假定视图名称与操作名称相同。

(P040)

视图引擎的调用会依照它们的注册顺序,并且在默认情况下, ASPX 引擎会优先于 Razor 引擎。若要修改这一顺序,需要在应用程序启动时清除引擎集合,再重新按喜欢的顺序添加引擎。

(P041)

视图无非是用来生成 HTML 内容的模板。

(P042)

在 ASP.NET MVC 中,把需要传递到视图的数据在一个容器对象中进行分组,再将该容器对象作为选择了该视图的控制器调用中的参数进行传递。

ASP.NET MVC 使你能够在对 View 方法的调用中直接传递容器对象。在任何情况下,都有两个预定义的字典可用于控制器方法填充数据。它们分别是 ViewData 字典和 ViewBag 字典。

需要记住的是,在理想的情况下,视图对象不需要自行检索数据;它必须处理的唯一数据只是从控制器所接收的数据。

指定母版视图是比较容易的。可以使用视图引擎所支持的规则,也可以在从控制器中选择下一个视图时将母版视图的名称作为参数传递给 View 方法。

(P043)

在 ASP.NET MVC 中等同于 服务器控件的技术是什么?答案就是 HTML 帮助器。

HTML 帮助器就是简单的 HTML 工厂。

HTML 帮助器的内部机制就是将文本累加到一个 StringBuilder 对象中。

(P044)

每个 HTML 帮助器均有一大堆重载来指定特性值和其他相关信息。

一个形如 xxxFor 的帮助器与基础版本的不同之处在于它只接受 lambda 表达式。

(P045)

默认情况下, BeginForm 会呈现一个回发到相同 URL 并随后回发相同控制器操作的表单。

使用 BeginForm 方法的其他重载,可以指定目标控制器的名称和操作、该操作的任意路由值、 HTML 特性,甚至可以指定是否需要表单执行 GET 或 POST 动作。

BeginRouteForm 的作用与 BeginForm 差不多,但有一点不同,它可以从任意一组路由参数开始而生成一个 URL 。换句话说, BeginRouteForm 并不局限于基于控制器名称和操作的默认路由。

(P046)

能在表单中使用的所有 HTML 元素都有一个 HTML 帮助器来加速开发。

从功能的角度来看使用帮助器与使用普通的 HTML 之间并无差别。

拥有将验证消息与输入字段相关联的工具。如果指定的字段中包含错误,则可以使用 Html.ValidatiobnMessage 帮助器来显示验证信息。该消息可以通过帮助器的一个附加参数显式指示出来。所有的验证消息之后会通过 Html.ValidationSummary 帮助器聚合与显示出来。

(P047)

ActionLink 帮助器是 ASP.NET MVC 视图中最常用到的一个。

通常情况下,操作链接需要链接文本、操作名称和可选的控制器名称。

RouteLink 帮助器的工作方式差不多相同,但它不需要指定操作。有了 RouteLink 帮助器,就可以使用任何已注册的路由名称来确定生成的 URL 的模式了。

若要生成基于控制器和操作数据的链接,则可以使用 UrlHelper 类。

UrlHelper 类有几个方法的作用类似于 ActionLink 和 RouteLink 。它们的名称是 Action 和 RouteUrl 。

(P048)

可以使用 Partial 或 RenderPartial 帮助器方法来插入一个部分视图。这两种方法都采用该部分视图的名称作为参数。两者的唯一区别是 Partial 返回一个字符串,而 RenderPartial 会写入到输出流,并返回空。因此,两者的用法稍有不同。

部分视图包含在视图中,但会被视作完全独立的实体。

为一个视图引擎编写一个视图而该视图的部分视图却需要另一个视图引擎的情形是完全合理的。

(P049)

有了模板化帮助器,就不会失去对用户界面的控制了;简言之,模型中的特性建立起一系列的规则,将你从繁重的重复劳动中解放了出来。

在 ASP.NET MVC 中,有两个基本的模板化帮助器 : Editor 和 Display 。

ViewBag 是在被定义为动态类型的 ControllerBase 类上定义的属性。

(P050)

lambda 表达式不支持动态成员,因此不能用于将数据传递到 ViewBag 字典。

Editor 帮助器的目的是让你编辑指定值或对象、该编辑器会识别它所获取的值的类型,并挑选量身定做的模板进行编辑。

Editor 帮助器极其擅长处理复杂类型。它通常会遍历每一个公共属性,并为子值建立一个标签和编辑器。

(P051)

TagBuilder 类生成 HTML 标记的文本,从而可以通过串联标签而非普通的字符串以构建大块的 HTML 。

HTML 帮助器预期会返回一个已编码的 HTML 字符串。

(P052)

所有的原生 HTML 帮助器都会被重构以返回 MvcHtmlString 。

MvcHtmlString 类型是对包含 HTML 内容的字符串的一个智能封装,它公开了 IHtmlString 接口。

IHtmlString 的目的是什么?在 ASP.NET 中试图对实现 IHtmlString 的对象进行 HTML 编码会造成空操作指令。

(P054)

根据 Razor ,视图模板就是一个 HTML 页面加上几个占位符和代码片段。

(P056)

Razor 视图模板实质上是一个 HTML 页面加上几个代码段,也称为代码碎块。

Razor 代码块的标志是开头使用 @ 字符。更为重要的是,不需要显式关闭这些代码块。

(P057)

Razor 模板就是带有封装了可执行语句和 HTML 帮助器的 @ 表达式的普通 HTML 标记文件。

可以通过在 @{code} 块中进行封装来在任意位置插入整段的多行代码。

(P058)

来源于表达式的单个语句可以通过使用括号在同一表达式中组合。

在你需要放置一个函数调用时,括号也起作用。

(P059)

Razor 视图引擎在使用时,生成的视图对象是一个在 System.Web.Mvc 程序集中定义的 WebViewPage 类的实例。这个类集成了解析标记和呈现 HTML 的逻辑。该类上的公共属性对于在实际模板中编写的任何代码碎片都可用。

(P060)

控制器能以各种方式将数据传递给视图。它可以使用全局字典,如 ViewBag 或 ViewData 。更好的是,控制器可以使用为特定视图量身定做的强类型对象。

要从代码碎块中使用 ViewBag 或 ViewData ,不需要采取任何特别措施。只需要编写读入或写进字典的 @ 表达式即可。相反,要使用强类型的视图模型,需要在模板文件的顶部声明实际的类型。

通过使用 Model 属性访问视图模型对象中的属性。

如果 Model 引用了具有子属性的对象,那么可以使用 Model.Xxx 来引用每个子属性。

在 Razor 中,布局页面扮演着 Web Forms 中母版页的角色。布局页面是一个标准的 Razor 模板,视图引擎会将定义的任意视图呈现出来,从而将外观和感觉与网站版面统一起来。

(P061)

使用 Razor ,可以在 Views 文件夹中定义一个特殊文件,该文件在每个视图构建和呈现之前进行处理。这个文件被称为 _ViewStart.cshtml ,是任何与视图相关的启动代码的理想容器,其中包括决定使用哪个布局的代码。

执行 RenderBody 方法会导致实际视图中的任何代码都注入布局模板。

实际视图模板中的代码会在视图和布局合并之前进行处理。

(P062)

从 ASP.NET MVC 4 开始,波浪号 (~) 通过 Razor 引擎自动扩展,虽然 Url.Content 的使用依然受到支持,但已不再必要。

在布局模板中,通过在希望节所出现的位置放置一个对 RenderSection 的调用来定义注入点。

每一个节都由名称标识,并能标记为可选。

如果视图模板不包含所需的节,会得到一个运行时异常。

(P063)

可以在 Razor 视图模板的任何位置定义节的内容。

WebViewPage 类提供了一个方便的 IsSectionDefined 方法,可以用在 Razor 模板中确定给定的内容是否已被指定。

请记住节的名称不区分大小写。

可以对 Razor 布局进行任意程度的嵌套。

(P066)

ASP.NET MVC 设计的黄金法则声明,视图会接收但不会计算它要显示的任何数据。可以通过三种非独占性的方式传递数据 : ViewData 字典、 ViewBag 字典以及量身定做的容器对象,也常被称作视图模型对象。

ViewData 属性直接由 Controller 类公开,它是 名称-值 字典对象。其编程模型类似于使用 Session 或其他 ASP.NET 内部对象。

存储在字典中的任何数据都会被当作一个对象来处理,需要转换、装箱、或这两者一起以便被视图所用。可以按需在字典中创建条目。字典的生存周期与请求的生存周期是一样的。

请记住,不会被限于仅在 ViewData 字典中存储字符串。

ViewData 字典非常适合于简单的解决方案和相对较短生命周期的应用程序。随着字典条目数量和视图数量的增长,维护会成为一个问题,因此寻找其他选项时应该摆脱 ViewData 。

(P067)

ViewBag 属性也在 Controller 类上进行定义,它提供了一个更加灵活的将数据传递给视图的工具。

基于动态类型的任何表达式都会被编译成在运行时解释。

ViewBag 的语法比 ViewData 的语法更为简洁,但就我看来,这就是所有的区别了。

ViewBag 至少需要是 ASP.NET MVC 3 和 .NET 4 ,而 ViewData 的适用范围是 ASP.NET MVC 的任何版本和 .NET 2.0 。

(P068)

处理软件中复杂性的唯一证明有效的方法是合适的设计。

为每个视图定义一个对象模型可以帮助跟踪视图的真实所需。建议为每一个添加到应用程序中的视图定义一个视图模型类。

视图模型对象是一个只有数据而 (几乎) 没有行为的普通数据传输对象。

(P069)

ASP.NET MVC 基础架构保证了 ViewData 和 ViewBag 始终可用于视图对象而无需开发人员的任何干预。而自定义视图模型对象就不是这样了。

当使用一个视图模型对象时,必须在视图模板中声明该视图模型类型。

要在视图模板中检索视图模型对象,可以使用在 WebViewPage 和 ViewPage 上都定义过的 Model 属性。

(P070)

虽然每个视图都应该有自己的模型对象,但限制要处理的类的数量通常是一个好选择。要在多个视图中重用模型类,往往需要生成一个类的层次体系。

视图模型类最终是为视图建模而不是为数据。

对于至少中等复杂程度和中等持续时间的任何 ASP.NET MVC 应用程序来说,强类型视图模型是唯一安全而可行的解决方法。

【第03章】

(P075)

通常存在至少两种完全不同的模型 : 领域模型和视图模型。

“领域模型” 描述在中间层使用的数据,预期会为填充业务领域的实体和关系提供可靠的表示。这些实体一般通过数据访问层来持久保存,并且通过实现业务流程的服务来使用。

视图模型只描述了表示层中正在处理的数据。

应该始终意识到在两个不同层中运行着两种不同模型的事实。

(P076)

要将输入数据传递给控制器,需要以某种方式把数据封装起来。

(P078)

输入模型提供了正在提交到控制器的数据的表示。视图模型提供了正在视图中进行处理的数据的表示。最后,域模型是在中间层中操作的域特定实体的表示。

类的最终结构和关系图取决于你自己。

模型绑定是指将通过 HTTP 请求所提交的值绑定到控制器方法所用的参数的过程。

(P079)

默认模型绑定器使用基于规则的逻辑,将提交的值的名称与控制器方法中的参数名称相匹配。

默认模型绑定器会使用所有已注册的值提供程序将提交值与方法参数进行匹配。

可以在方法签名上列出的参数的数目没有上限。但是,一个容器类往往比一个各种参数的长列表要好。对于默认模型绑定器,无论列出一系列参数,还是复杂类型的一个参数,结果几乎相同。这两种方案都完全受到支持。

(P083)

模型绑定器能够处理该复杂类型,就像它处理单个值的情况一样。

对于声明的类型中的每一个公共属性,模型绑定器都会寻找其关键字名称与属性名称相匹配的提交值。匹配不区分大小写。

(P089)

默认绑定器通过匹配与参数名称一起上传的输入文件元素的名称来实现绑定。但是参数 (或参数类型上的属性) 必须声明为 HttpPostedFileBase 类型。

(P090)

默认情况下,任何 ASP.NET 请求都不能超过 4MB 。这个数字包含了所有的上传、标头、正文以及正在传输的任何内容。可以通过 web.config 文件中 httpRuntime 部分的 maxRequestLength 条目来配置各个级别的上传阈值。

(P092)

默认的模型绑定器会强制将请求参数命名为在目标操作方法上与形参相匹配的指定名称。

(P095)

请记住,编写模型绑定器时,并不局限于仅从提交数据中获取用于模型的信息,虽然这代表了大多数常见情形。可以从任何位置 (比如从 ASP.NET 缓存和会话状态中) 获取信息,解析并将它存储在模型中。

(P096)

局部绑定器总是优先于全局定义绑定器。

(P102)

模型绑定器提供了将表单提交值反序列化为简单类型和复杂类型的完全控制权。

【第04章】

(P103)

HTML 帮助器,可以自动为任何简单或复杂的类型创建简单而实用的视图和编辑器。

数据批注,可以用声明方式设置对某字段的内容及其显示行为的预期。

模型绑定器,可以将提交的值序列化为对服务器端的处理更为适用的对象。

(P112)

PRG 模式建议每个 POST 请求在经过处理后,以重定向到一个通过 GET 访问的资源而结束。

(P117)

在 ASP.NET MVC 中,模板化帮助器使用与类成员相关联的元数据来决定如何显示或编辑数据。元数据通过元数据提供程序对象读取;默认的元数据提供程序会从数据批注特性中获取信息。

(P118)

数据批注是特性,而特性通常不包含代码。它们仅仅表示其他模块需要使用的元信息。通过使用数据批注,模型对象就被元数据修饰了。这并不能预计产生任何的可见影响 : 一切都取决于其他组件如何使用元数据。

数据批注包括描述性特性和验证特性,描述性特性指示侦听器如何显示或编辑数据,验证特性指示侦听器如何验证模型类的内容。

(P119)

注意如果没有使用 DisplayForModel 或 EditorForModel 自动生成输入表单,就不需要数据批注。

(P122)

显示 / 编辑 帮助器在很大程度上可以自定义。任何自定义的模板都包含一个位于 Views[Controller]\DisplayTemplates 文件夹用于显示帮助器的自定义视图,和一个位于 Views[Controller]\EditorTemplates 文件夹用于编辑帮助器的自定义视图。

(P141)

当开启客户端验证时,所有内置的数据批注特性都会获得一个客户端行为,并尽可能在浏览器中执行其在 JavaScript 中的验证。如果验证失败,就不会向 Web 服务器发出请求。

【第05章】

(P151)

在很大程度上,可以把 ASP.NET MVC 视为经典 ASP.NET 运行时环境的特别版本,其仅用于支持不同的应用程序和编程模型。

ASP.NET MVC 应用程序对构成 ASP.NET 生态系统的任何内置组件都具有完全访问权,包括 Cache 、 Session 、 Response ,以及身份验证和错误处理层。

(P152)

HttpResponse 对象的公共接口可以设置 cookies 和内容类型、附加标头、以及针对响应数据的缓存将指令传递给浏览器。另外, HttpResponse 对象还有助于重定向到其他 URL 。

在 ASP.NET 中,当调用 Response.Redirect 时,会向浏览器返回一个 HTTP 302 代码,表明所请求的内容现在可以从另一个指定位置获得。

(P155)

ASP.NET MVC 基础架构在内部使用会话状态,也可以在代码中这样做。

(P156)

ASP.NET MVC 基础架构会使用会话状态来保持 TempData 字典的内容。

在会话状态中存储的任何数据都会作为一个 Object 返回。

(P157)

在 ASP.NET 中,内置的缓存功能是通过 Cache 对象实现的。 Cache 对象是在每一个 AppDomain 基础上创建的,并且在 AppDomain 运行期间它仍然保持有效。

(P161)

MemoryCache 类继承自基类 ObjectCache 。通过派生你自己的缓存对象,可以控制内部存储以及缓存数据的管理。

请记住, ObjectCache 及其派生的类型并不适用于提供分布式缓存功能。

(P165)

来自微软的关于异常处理的另一项主要原则是,对于一般的错误使用内置类型,对于特定于你正在创建的应用程序,要创建应用程序专属的异常类型。

(P171)

在 ASP.NET 开发人员之间比较流行的工具是错误日志记录模块和处理程序 (Error Logging Modules And Handlers , ELMAH) 。 ELMAH 本质上是由一个 HTTP 模块构成,一旦配置,就会拦截应用程序级别的 Error 事件,并根据大量后端存储库的配置将其记录下来。

当自定义错误标志关闭时,拦截模型绑定错误的唯一办法就只能是借助于 Global.asax 中的集中式错误处理程序。

(P173)

全部捕获规则需要在路由列表的最底部运行。

在 ASP.NET MVC 中并没有现成的 Error 控制器,但强烈建议创建一个。

(P174)

通过使用错误控制器,可以提高应用程序的友好性,并为搜索引擎对其进行优化。

全部捕获路由就是一个为 URL 选出的不匹配任何其他路由的路由。

(P181)

在 ASP.NET MVC 中,当你只想呈现视图的时候要使用 Html.RenderPartial ;当你想要取回 HTML 标记并自己编写到流时,要使用 Html.Partial 。

【第06章】

(P190)

Windows 身份验证很少应用于实际的互联网应用程序。

表单身份验证是最常用的收集和验证用户凭据的方式,例如使用用户账户数据库进行验证。

在 ASP.NET MVC 和 Web Forms 中,是通过根 web.config 文件中的 部分选择身份验证机制的。

默认情况下, ASP.NET MVC 应用程序会被配置为使用 Forms 身份验证。

当要限制对某操作方法的访问时,请使用 Authorize 特性,并确保只有通过身份验证的用户可以执行它。

如果将 Authorize 特性添加到控制器类,那么控制器上的任何操作方法都将需要进行身份验证。

(P191)

绝对不要将 Authorzie 特性用作全局筛选器。

AllowAnonymous 方法派上用场的情况是,当把 Authorzie 应用在类级别时,之后需要启用对一些方法的自由访问,尤其是登录方法。

【第07章】

(P231)

工作线程服务或应用程序服务属于系统的应用程序层,应用程序层是实现从用例中所产生的应用程序逻辑的地方。这一层不能重用,因为它是特定于应用程序 (和前端) 的。可重用性要推至下一层的域层中。意即核心功能是可重用的 (也就是域) ,但呈现工作流会特定于应用程序。

ASP.NET MVC 是一个旨在方便测试和推动像关注点分离 (SoC) 和依赖注入 (DI) 这样的重要原则的框架。

(P239)

层 (layer) 是指逻辑上的分离,而层级 (tier) 是指物理上的分离。

(P240)

表示层截获请求并将其传递到应用程序层。应用程序层 (也称为服务层) 是应用程序实现用例的区段。在这一点上,它特定于每个应用程序并且不可重用。应用程序层将端点公开给表示层,并将它从系统的其他部分解耦。应用程序层会安排域服务和外部服务,以及可能用到的特定业务的企业组件。最后,基础架构层会封装对数据的访问。

(P241)

就 ASP.NET MVC 而言,应用分层架构模式意味着将请求的响应结果委托给工作线程服务,接着,会联系后端获得响应。响应包含了表示层所需格式的数据。

应用程序层包含用例的实现,可以将其看作起编排作用的组件的聚合。

(P242)

对象模型、域模型和实体模型是常常可以互换使用的相似术语。

对象模型是普通、泛型的对象集合。

域模型是特殊类型的对象模型,其中的每一个类都是 POCO 、聚合根会被识别出、工厂会被用于构造函数、并且大多数时候值类型往往会取代基元类型。

实体模型基本上是一个表明类 (大多是贫血类) 的集合的实体框架术语。可能是 POCO ,也可能不是。贫血类有数据,但几乎没有行为。

(P243)

专注于视图的对象模型是基于数据传输对象 (data-transfer objects , DTO) 的。 DTO 是一个普通的容器类 (只有数据,没有行为) ,用于在层、层级之间甚至同一层之内传递数据。

基础架构层基本上就是数据库层,重命名后降低了放在数据和模型上的焦点和重点。

(P244)

基础架构层处理持久性,它由存储库类构成,每个重要实体 (或者,也可以称为聚合根) 配有一个。存储库类使用一个指定的存储 API 来实现持久性。存储库类的实现在逻辑上属于数据访问层。存储库类会聚集多个服务于实体数据访问需求的方法。在存储库中,通常可以找到读取、添加、删除和更新数据的方法。

存储库向应用程序层公开接口并在内部使用存储 API 。

在每个实体的基础上 (确切地说,只是主要实体) 创建的存储库是通往实际持久层的大门。

(P245)

高级别的类应该总是依赖于它们所需低级别类的抽象。

(P246)

服务定位器模式定义了一个组件,它知道如何检索应用程序可能需要的服务。调用方无须指定具体的类型;调用方通常表示接口、基类型或者是以字符串或数字编码形式存在的服务昵称。

(P247)

依赖注入模式表明你要以这样一种方式设计类 : 它从外部接收其所有依赖性。

(P248)

在类中使用依赖注入的时候,开发人员必须作出的一个关键决定是,如何以及在何处让代码注入。有三种方式可以把依赖性注入到类中 : 使用构造函数、一个可设置的属性、或一个方法的参数。这三种方式都有效,最终的选择取决于你。一般的共识是将构造函数用于必要的依赖性, setter 方法用于可选的依赖性。

(P249)

IoC 容器是一个专门创建用来支持 DI 的框架。

【第08章】

(P257)

TempData 字典用来存储从逻辑上讲属于当前请求的数据,但必须要跨越一个重定向来使用。

根据 PRG 模式,应该用一个 GET 重定向到显示结果的视图来终止一个 POST 请求。

默认情况下, TempData 字典会将其内容保存到会话状态。直接使用 Session 与借助 TempData 的主要区别是,任何存储在 TempData 中的数据都会在连续请求终止后自动清除。换句话说,数据驻留在内存中只会用于两个请求 : 当前请求和下一个重定向。归根结底, TempData 带给内存的压力更小。

(P260)

对于从头开始构建的新系统, DI 是该优先考虑的;它会使代码保持得更整洁,更便于阅读和测试。使用 DI ,依赖性在类的签名中是显式的。不过,周边框架要求对依赖性的准备和注入负完全责任。对于现有系统, SL 更容易接入使用,因为它只会要求你将调用封装在一个公开接口的黑盒中。

(P261)

单独注册的组件对应用程序来说一定是唯一的一个组件。多次注册的组件可以注册多个实例并向系统的其他部分公开。

(P266)

每个控制器方法可以用多个筛选器来修饰。

可以把筛选器应用于单个方法,也可以应用于整个控制器类。如果将筛选器应用于控制器类,则会对所有由控制器公开的操作方法产生影响。与此相反的是,全局过滤器是在应用程序启动时注册并自动应用于任何控制器类的任何操作。

(P268)

在 ASP.NET Forms 中,压缩一般是通过 HTTP 模块拦截请求和压缩响应来实现的。你还可以在互联网信息服务 (IIS) 级别开启压缩。这两个选项在 ASP.NET MVC 中都好用,决定使用哪一个选项取决于你。

ASP.NET MVC 提供了特别容易实现的第三个选项 : 一个对压缩事件进行设置的特定操作筛选器。用这种方式,可以在不必编写 HTTP 模块的情况下控制某种特定的 URL 。

【第09章】

(P305)

从设计的角度看,可测试软件本质上是更好的软件。

当你把控制性、可见性和简单性应用于软件开发过程中时,最终会获得只通过约定接口就能进行交互的相对较小的构建块。

(P307)

指示你的类面向接口运行而不是面向实现运行是现代软件开发的五大支柱之一。

开发的五项原则常常被总结为首字母缩写的 SOLID 一词。

(P308)

在现代软件中,编写面向接口而非面向实现的代码这一概念已被广为接受和应用了,但它也常常被另一个更具体的概念所掩盖 : 依赖注入。

可以说基于接口编程的整个概念是在依赖反转原则 (DIP) 中进行硬编码,且依赖注入是应用 DIP 的一种流行设计模式。

(P313)

测试方法的典型设计可总结为三个缩写的 “A” ,分别代表着设置、作用、断言。

【第10章】

(P339)

ASP.NET Web API 是一个新的框架,其明确意图是支持和简化可以由各种客户端所使用的 HTTP 服务的构建过程;尤其是 HTML 网页和移动应用程序。

Web API 是一个精心设计的框架,它用于为 .NET 应用程序构建 RESTful (具象状态传输) 和远程过程调用 (RPC) 风格 HTTP 服务。

(P340)

WCF 的最初构想是透过种类繁多的传输层来支持 SOAP 和 WS-* ,这些传输层包括 TCP 、 消息队列 (MSMQ) 、命名通道,以及最后也是最重要的一个 : HTTP 。

(P341)

Web API 非常适合于现在似乎常见的应用程序场景 : 客户端应用程序需要调用远程后端来下载数据或请求处理。客户端应用程序可以采用多种形式 : JavaScript 密集型网页、富客户端或移动应用程序。

JSON 格式是在客户端和 HTTP 服务之间序列化对象的理想格式。

(P343)

Web API 基础架构与 ASP.NET MVC 基础架构是独立的,不具有隐藏的依赖性。

(P344)

Web API 模块是一个独立的基于 HTTP 的服务,能够被许多应用程序托管,而 ASP.NET MVC 应用程序大概是最常用的宿主类型。

REST 被定义为专注于通过 Web 协议、特别是 HTTP 协议识别和处理资源的一种架构风格。

(P351)

构建一个在 ASP.NET MVC 应用程序内部的 Web API 模块是由该 ASP.NET MVC 应用程序托管的。

要使用 Web API 模块,你只需要使用 ASP.NET MVC 站点即可。

从 Web 客户端内部, Web API 前端就是一个简单的 HTTP 端点集合。可以使用 jQuery 工具来放置 HTTP 调用,或者使用 KnockoutJS 工具来呈现出对象集合。

(P352)

$.ajax jQuery 工具让你能够指定 HTTP 动词和标头。这样一来,你就能够轻易地作好调用 Web API 后端上的任意类型操作的调用设备。

由于 Web API 模块是一个普通的 HTTP 前端,因此从一些 .NET 服务器端代码内部调用它就与从任何其他类型的远程端点调用没什么区别。

(P354)

作为使用 WebClient 进行远程调用的替代选项,可以使用最新的 HttpClient ,它是老式可靠的 WebClient 客户端的修改及更丰富的版本。 HttpClient 是在 System.Net.Http 名称空间中定义的。

【第11章】

(P369)

一个完整的 Ajax 应用程序是将页面交互减少到最低限度的 Web 应用程序。

作为 ASP.NET MVC 开发人员,你要准备好编写越来越多的 JavaScript 代码。

(P371)

一个设置为 undefined 的变量不会被任何代码触及到;而包含 null 的变量会通过你代码中的一些路径被分配到某个值。

如果在未赋值的变量上运行 typeof ,就会得到 undefined —— 它本身就是一种独特的类型。如果在被赋予 null 值的变量上运行 typeof ,你会获得 object 。

在定义变量时,你应一直使用 var 关键字作为对解析器和你自己的提示。

var 关键字并非绝对必要,但强烈建议使用。

如果通过使用 var 对变量进行了声明,那么在函数内定义的变量就会仅限于这个函数。如果没有声明,变量会被视为全局的,但它们会保持 undefined ,直到函数执行一次。

在全局范围中声明的变量都是全局变量,无论是否使用 var 。

(P372)

JavaScript 运行时环境会将全局变量存储为通过 this 关键字引用的隐藏对象的属性。

缺失 var 的话,你最终获得的就是全局变量;在赋值中键入错误的变量名称,也会得到一个新的全局变量。

(P380)

嵌套 jQuery 函数被映射成浏览器窗口对象的扩展并且用流行的 $ 函数作为别名。该函数具有以下原型 : function(selector, context) ,选择器表示在 DOM 上运行的查询表达式;上下文表示从 DOM 的哪个部分运行该查询。如果没有指定上下文,则 jQuery 函数会在整个页面 DOM 中查找 DOM 元素。

jQuery 函数通常返回一个包装集 —— 也就是 DOM 元素的集合。非常棒的是,这个包装集仍然是 jQuery 对象,可使用相同的语法来查询,并产生一系列查询。

jQuery 库的主要目的是运行针对 DOM 的 (明智) 查询并执行针对返回项的操作。

一个查询结果就是一个包装集。包装集是包含 DOM 元素集合的对象。元素会按照其在原始文档中出现的顺序被添加到集合中。

(P381)

包装集并不是一个特殊的数据容器。“包装集” 是一个表明查询结果的特定 jQuery 术语。

为了循环遍历包装集中的元素,你要使用 each 函数。此函数会将一个函数用作参数并基于每个元素调用它。

可以使用 length 属性来读取包装集的大小。也可以使用 size 函数,但 length 属性会稍微快一点。

请注意, get 函数 (以及索引) 会中断 jQuery 的连贯性,因为它会返回 DOM 对象或 DOM 对象的数组。你不能进一步将 jQuery 函数应用于 get 调用的结果。

(P382)

在 jQuery 中,你有三种基本类型的选择器 : 基于 ID 的选择器、层叠样式表 (CSS) 和标签名称。

ID 选择器会通过 ID 来选取 DOM 元素, ID 选择器通常只会选择一个元素。

可以串联两个或更多个选择器来组成更特定的选择器。

通过多个运算符就能实现串联。

(P386)

包装集上的操作 :

  1. DOM 操作 —— 创建 DOM 树、添加 / 移除 元素,或修改现有元素;

  2. 绑定事件 —— 将处理程序绑定和解除绑定到由 DOM 元素发起的事件;

  3. 样式化 —— 为所选元素应用、移除或切换 CSS 类并得到或设置单个 CSS 属性;

  4. 可见性 —— 使用过渡效果及持续时间显示和隐藏 DOM 元素;

(P387)

bind 和 unbind 这对函数用于将回调函数附加到指定事件。

unbind 函数并不会移除那些通过 onXXX 这样的特性直接在标记中插入的处理程序。

jQuery 库还定义了不少直接函数来绑定指定事件。

事件处理程序会接收一个 jQuery 内部对象 —— Event 对象。该对象会提供一个用于事件的统一编程接口,该接口与万维网联盟 (W3C) 的倡议密切相关,并且它解决了某些浏览器造成的轻微不同实现所引起的差异性问题。

(P388)

Event 对象的属性特征包括鼠标坐标、事件的 JavaScript 时间、使用的是哪个鼠标按钮,以及事件的目标元素等。

如果选择动态绑定而非普通绑定,就确保了任何动态添加的能匹配选择器的元素都会自动附加相同的处理程序。

从 jQuery 1.7 开始,你就该通过 on 和 off 函数来操作动态绑定了。

如果使用的 jQuery 版本比 1.7 低,就要使用 live 和 die 方法作为 on 和 off 的替代。其语法稍有不同,因为 live 方法不会将选择器用作参数,相反它会直接应用于选择器。

DOM 中的 document 根对象公开了一个只读的 readyState 属性,这就是为了让你获知 DOM 的当前状态并获悉何时你的页面准备就绪,能够开始执行脚本。使用 readyState 属性是一种绝对可行的方式,只是它有一点麻烦。

(P389)

可以在页面或视图中拥有多个 ready 调用。指定多个 ready 调用时, jQuery 会将指定函数推送到内部堆栈并在 DOM 实际准备好之后按顺序对其进行处理。

ready 事件仅会在文档级别触发;你不能为单个元素或包装集中的任何元素集合定义该事件。

onload 事件是在 HTML 和所有辅助资源都加载之后调用的。 ready 事件是在 DOM 初始化之后调用的。这两个事件的运行顺序可以随意安排。 onload 事件不能确保页面 DOM 已加载; ready 事件不能确保所有资源已经加载。

(P390)

无侵入性 JavaScript 建立了一个基础原则 —— 任何网页中的任何行为都必须是可注入的依赖项并且不是一个构建块。

限制 UI 依赖项影响的一个方法是使用模板和 KnockoutJS 这样的特设库来呈现页面。
**