在这一章中,我们将学习如何使用基架快速搭建和运行一个简单的Microsoft ASP.NET MVC Web站点。在我们马上投入学习和编码之前,我们首先了解一些有关ASP.NET MVC和Entity Framework的背景信息,然后再详细学习如何搭建开发环境。
1.1 MVC和ASP.NET MVC
这本书涵盖Microsoft版本的MVC,即ASP.NET MVC。在写本书的时候,ASP.NET MVC的生产版本为MVC5,因此,本书的示例代码都使用ASP.NET MVC 5编写。在本书中有一章内容涵盖ASP.NET Core 1.0 MVC (MVC 6)。
MVC这三个大写字母分别表示Model-View-Controller(模型-视图-控制器),是一种广泛使用的软件开发设计模式。ASP.NET MVC由以下基本模块组成:
- Models:是一些表示应用程序数据的类,这些类通常称之为普通CLR对象(Plain Old CLR Objects, POCOs)。这些类也用来封装和执行一些业务逻辑,比如,我们稍后看到的购物车的业务逻辑。
- Views:是一些用于生成HTML的模板文件,生成的HTML文件将会被发送到浏览器。视图(View)通常用于展示从模型(Model)获取的数据。尽管视图(View)可以包含一些决定哪些HTML可以被生成的逻辑,但不应该包含任何业务逻辑。
- Controllers:是一些用于处理输入请求的类,这些类可以从模型(Model)获取数据,并将这些数据传递给视图(View),最终在视图(View)生成的HTML中显示。控制器(Controller)可能会包含一些根据请求中的某些信息来过滤数据的逻辑。比如,我们可以根据传递给控制器(Controller)方法的参数(该参数的值来自于客户端请求)来生成查询。
译者注:在本书后续翻译过程中,将会把Model翻译成模型、View翻译成视图、Controller翻译成控制器,不再使用英文描述。
在本书中的示例代码有助于加深我们对模型、视图和控制器定义的理解,并在合适的时机使用更加高级的概念,比如视图模型(View Model)。
MVC起源于70年代后期的Smalltalk项目,从此之后不断被更新并应用于多个技术领域。MVC的主要原则是要创建一个可分层的、可测试的以及可维护的应用程序体系架构。MVC的特点之一就是比较适合做单元测试,这主要得益于模型、视图和控制器的划分。单元测试在本书中没有涉及,如果想对单元测试有一个深入的理解,推荐大家阅读Adam Freeman的PRO ASP.NET MVC(中文名称:精通ASP.NET MVC 5)一书。
1.2 Entity Framework和Code First(代码优先)
Entity Framework (EF)是一个对象关系映射(object relational mapping, ORM)框架,该框架是.NET Framework的一部分。本书将通篇使用Entity Framework的Code First方法编写类及它们之间的关系,然后在不使用任何SQL语句的情况下,生成数据库及对应的表。在编写本书时,Entity Framework可用版本为EF 6,然而在本书中有一章会涉及使用EF 7版本。在本书中的所有示例代码使用的都是SQL Server,但是,Entity Framework也可以用于其他关系型数据库,比如Oracle。
1.2.1 将Code Frist应用于现有数据库
正如前面锁提到的,Code First是使用Entity Framework的一种方法,使用该方法可以允许我们首先编写Model类,然后根据模型类来生成数据库及对应的表,这比较适合数据库不存在的新建项目。但是,从EF 6.1开始,Code First也可以用于已存在的数据库,并且可以根据已存在的数据库生成Model类。这个主题我们会在本书后面章节深入讲解。
1.3 Web开发的软件需求
本书所使用的是Visual Studio 2015 Community版本,这个可以在Microsoft站点免费下载,该版本几乎包含了Visual Studio 2015付费版本的所用功能,我们将使用这个版本编写本书的所有代码。在本书中我们还将使用Google的Chrome浏览器来运行和测试我们编写的代码,之所以使用Chrome浏览器,是因为该浏览器提供了非常优秀的针对开发人员工具。我们还推荐大家最好在拥有4GB内存的Windows PC机上运行Visual Studio。
我们可以在以下链接中下载Visual Studio 2015 Community版本:https://www.visualstudio.com/zh-hans/downloads/
注意:当我们安装Visual Studio 2015 Community的时候会自动安装SQL LocalDB,但是,在Windows 10系统上,可能需要我们单独安装SQL LocalDB。SQL Server Express可以在以下链接中下载:https://www.microsoft.com/zh-CN/download/details.aspx?id=42299
1.4 创建项目
我们要做的第一件事就是使用Visual Studio创建一个使用ASP.NET MVC和Entity Framework的Web项目。启动Visual Studio,然后在菜单栏中依次选择【文件】->【新建】->【项目】,在弹出的“新建项目”窗体中,确保我们选择了【已安装】->【模板】->【Visual C#】->【Web】,然后选择“ASP.NET Web应用程序”项目类型,如图1-1所示。选择.NET Framework 4.6.1作为我们.NET的版本。
图1-1:选择AS.NET Web应用程序类型
输入名称BabyStore,然后点击“确定”按钮继续。我们将看到另一个窗体,如图1-2所示。在这个窗体中,我们在ASP.NET 4.6.1模板中选择MVC,并确保身份验证为个人用户账户。
图1-2:选择MVC模板选项
点击“确定”按钮创建项目,一旦项目创建完毕,我们将会看到一个写有“你的ASP.NET应用程序”的页面。关闭这个文件,因为我们不会用到它。看一下在Visual Studio右侧的解决方案资源管理器,Visual Studio使用一个名为基架的过程自动生成了一个基本的MVC项目,我们将使用这个项目作为我们项目的入口点。
在解决方案资源管理器中展开Controllers、Models和Views文件夹,如图1-3所示。在Models文件夹下创建的文件我们将在后续的身份验证中用到。在这一章中,我们将学习HomeController类和它相关的视图。
图1-3:展开Controllers、Models和Views文件夹的解决方案资源管理器
1.4.1 查看Web站点
从Visual Studio菜单栏中,选择【调试】->【开始执行(不调试)】,Web站点将会在Visual Studio所使用的浏览器中打开,如图1-4所示。要想改变所使用的浏览器,只需要在Visual Studio菜单栏的第二行中的下拉列表中选择一个不同的浏览器即可。
注意:这个Web站点在运行时会使用Visual Studio赋给的一个随机端口。URL的通常模式为http://localhost:port/Controller/Action/AdditionalParameters(注:此处译者将http://localhost:port/Controller/View/AdditionalParameters中的View修正为Action)。当这个站点启动时,我们看到在URL中并没有显示控制器名或者动作方法名,这是因为在这个示例中,默认调用了Home控制器中的Index动作(Action)方法,而在Index动作方法中返回的是一个名为Index的视图。
图1-4:自动创建的MVC Web站点
1.4.2 主页是如何工作的
在图1-4中之所以显示主页,是因为我们向Web站点发送了一个URL为/Home/Index的请求,之所以这个URL没有在浏览器的地址栏中显示,是因为它是应用程序启动时的默认URL。如果我们在图1-4中的URL后面添加/Home/Index,然后回车,会发现所显示的页面没有发生改变。
上述URL告诉MVC Web应用程序,一个客户端请求被发送给Home控制器的Index动作方法。该动作方法在HomeController.cs文件中定义,如下面代码所示。该动作方法只包含一行简单的代码return View(),这行代码告诉Web站点要打开相对应的视图文件,在这个示例中,这个对应的视图文件是Views\Home\文件夹下的Index.cshtml。按照约定,ASP.NET MVC控制器的动作方法的默认视图文件可以在“Views\控制器名\动作方法名.cshtml”路径下找到。当然,我们可以改变这种约定,使得动作方法可以打开一个不同的视图,我们将在本书后续章节讲述。包含Index动作方法的Controllers\HomeController.cs文件的内容如下所示:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 7 namespace BabyStore.Controllers 8 { 9 public class HomeController : Controller 10 { 11 public ActionResult Index() 12 { 13 return View(); 14 } 15 16 public ActionResult About() 17 { 18 ViewBag.Message = "Your application description page."; 19 20 return View(); 21 } 22 23 public ActionResult Contact() 24 { 25 ViewBag.Message = "Your contact page."; 26 27 return View(); 28 } 29 } 30 }
Views\Home\Index.cshtml文件包含显示在主页中的与显示文本相关的HTML代码。在这个文件中,我们可以看到好几个HTML元素都带有class属性,这些class属性的值是Bootstrap的CSS类,这些类控制着Home页的布局。在后续章节中,我们将学习一些方法来改变Bootstrap以及如何在完全不使用Bootstrap的情况下重新设计我们的站点。
1.4.3 关于页面、联系方式页面和ViewBag
除了注册和登录这两个链接之外,在主页还有另外两个页面的链接:关于和联系方式,它们链接到的URL分别为/Home/About和/Home/Contact。
这两个URL链接遵循前面所讲的约定,即它们会分别调用Home控制器中的About动作方法和Contact动作方法,并且会加载Views\Home目录下的About.cshtml视图和Contact.cshtml视图。
如果我们检查在上面代码中的About方法和Contact方法,我们会看到在这两个方法中都有一行代码使用了ViewBag。
About动作方法包含的代码为ViewBag.Message = “Your application description page.”,Contact方法包含的代码为ViewBag.Message = “Your contact page.”,赋值的这两句话都显示在它们各自的视图中。
如果我们查看About.cshtml文件的代码,会发现其中有一行代码为<h3>@ViewBag.Message</h3>,视图使用这一行代码来显示ViewBag的Message属性的值。当在浏览器中显示时,我们会看到“Your application description page”这行文本,如图1-5所示。
图1-5:通过ViewBag由控制器传递给About视图的显示信息
ViewBag是一个动态对象,用于将数据传递给视图。由于其动态性,不推荐使用ViewBag给视图传递多个数据片段。ViewBag不是强类型,这意味着它可以包含任何数据,因此,我们在视图中使用它的时候没有智能提示功能。我们推荐使用强类型视图模型一次给视图传递多个数据片段。我们将在第三章讲述。
1.4.4 路由:Web站点是如何知道我们请求的是哪个控制器的哪个方法
ASP.NET MVC使用ASP.NET的路由控制应用程序如何解释URL。它也控制着传递进来的HTTP请求的目标是哪个控制器的哪个方法,从而显示哪个视图。为了解释它是如何工作的,我们查看一下前面创建应用程序的时候自动生成的默认路由。
打开App_Start文件夹下的RouteConfig.cs文件,在这个文件中的代码定义了应用程序以何种方式确定请求的URL和哪个控制器的那个动作方法相匹配。代码如下所示:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using System.Web.Routing; 7 8 namespace BabyStore 9 { 10 public class RouteConfig 11 { 12 public static void RegisterRoutes(RouteCollection routes) 13 { 14 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 16 routes.MapRoute( 17 name: "Default", 18 url: "{controller}/{action}/{id}", 19 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 ); 21 } 22 } 23 }
我们感兴趣的代码是routes.MapRoute方法,在第17行定义了一个名为“Default”的路由。代码url: "{controller}/{action}/{id}"指示站点期望第一个片段为控制器的名字,其次是动作的名字(匹配控制器中的方法名),然后是一个名为id的片段。我们待会就会讨论id片段,但是,现在我们集中精力在控制器和动作方法片段上。
default命名参数的值告诉应用程序当请求的URL中没有指定控制器或动作时,就使用其默认值,控制器的默认值是Home,动作的默认值是Index,由此会调用Views\Home下的Index.cshtml视图。这就是为什么当我们使用不带任何参数的URL时,Home页面会加载的原因。
1.4.4.1 使用可选URL的ID参数
为了完成这个非常简单的默认路由设置的概述,我们修改这个站点的让它使用可选的ID参数。
打开Controllers文件夹下的HomeController.cs文件,将其中的About方法的代码修改为下列代码:
1 public ActionResult About(string id) 2 { 3 ViewBag.Message = "Your application description page. You entered the ID " + id; 4 5 return View(); 6 }
我们告诉这个方法去处理传递给它的名为id的参数,这和默认路由设置中的可选的第三个参数名称相符合。ASP.NET MVC的模型绑定系统会自动将URL中的ID参数映射到About方法的id参数。我们将在后面的章节中详细讲述模型绑定。
现在不调试启动Web站点导航到关于页面,然后在URL的后面添加一个ID,如下所示:http://localhost:5073/Home/About/7。
这将把7作为ID参数的值传递给About方法,在该方法中我们将这个值添加到了ViewBag中,并在视图中显示出来。结果如图1-6所示。
图1-6:应用和处理一个可选URL的ID参数
ASP.NET MVC也自动匹配任何与方法参数相匹配的HTTP请求中的参数。这种匹配是不区分大小写的。比如,我们输入的URL为http://localhost:5073/Home/About?id=7将会返回和前面相同的结果,这是因为在查询字符串中的id参数自动匹配About方法的id参数。在第二章我们还会详细讲述。
1.4.5 布局页的作用
我们可能已经注意到了,当我们导航到不同的页面,这个站点显示一样的页头和导航菜单。这种效果是由布局页来完成的。在Views\Shared\_Layout.cshtml中的布局页的内容如下所示:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <meta charset="utf-8" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>@ViewBag.Title - 我的 ASP.NET 应用程序</title> 8 @Styles.Render("~/Content/css") 9 @Scripts.Render("~/bundles/modernizr") 10 11 </head> 12 <body> 13 <div class="navbar navbar-inverse navbar-fixed-top"> 14 <div class="container"> 15 <div class="navbar-header"> 16 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 17 <span class="icon-bar"></span> 18 <span class="icon-bar"></span> 19 <span class="icon-bar"></span> 20 </button> 21 @Html.ActionLink("应用程序名称", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) 22 </div> 23 <div class="navbar-collapse collapse"> 24 <ul class="nav navbar-nav"> 25 <li>@Html.ActionLink("主页", "Index", "Home")</li> 26 <li>@Html.ActionLink("关于", "About", "Home")</li> 27 <li>@Html.ActionLink("联系方式", "Contact", "Home")</li> 28 </ul> 29 @Html.Partial("_LoginPartial") 30 </div> 31 </div> 32 </div> 33 <div class="container body-content"> 34 @RenderBody() 35 <hr /> 36 <footer> 37 <p>© @DateTime.Now.Year - 我的 ASP.NET 应用程序</p> 38 </footer> 39 </div> 40 41 @Scripts.Render("~/bundles/jquery") 42 @Scripts.Render("~/bundles/bootstrap") 43 @RenderSection("scripts", required: false) 44 </body> 45 </html>
如果我们打开这个文件,我们会看到它包含了站点导航的一些HTML元素。在该文件中的@RenderBody()方法会将请求的视图内容插入到布局标记之中。
Views\_ViewStart.cshtml文件中的内容指定了Web站点使用Views\Shared\_Layout.cshtml文件作为主布局页。框架会将此文件的内容视为视图的一部分,因此可以有效地将其内容添加到每个视图的开始部分。下面的代码显示了_ViewStart.cshtml文件的内容,它指定我们的站点将使用哪个布局页。
1 @{ 2 Layout = "~/Views/Shared/_Layout.cshtml"; 3 }
提示:如果我们不想让一个视图使用_ViewStart.cshtml文件中指定的布局页,我们可以在这个视图文件的最上面添加@{ Layout = null; }。
1.5 小节
在这一章中,我们仅仅对ASP.NET MVC和Entity Framework做了一个非常简单的概览,并展示了在本书中我们将会一直使用的创建项目的方式,并解释了Web站点的基础,包括控制器、视图、ViewBag和路由。我们只所以在本章中做了简单描述,是因为在本书的后续章节中我们会详细解释这些概念,并指导我们快速开发一个数据驱动的Web站点。我们可能已经注意到了,在本章中我们没有涉及任何有关模型的知识,这是因为该部分知识是我们下一章的内容,在下一章我们将对我们的Baby Store站点添加一些产品和分类。