MVC系列1-MVC基础
终于决定写一个系列的文章了,最开始其实是准备写一下WPF的,因为我这两年一直在做WPF,对WPF的喜爱自然是无以言表。但是由于我所在的地区对WPF的普及不是很广泛,所以,被迫又开始做起来web,但是我又不想在传统的web froms上工作。固而开始研究MVC。在看完了一本入门级的MVC书籍之后,我又转而开始爱上MVC。它轻量,简洁,扩展性和对html的深度控制这些特性会打破ASP.NET的开发模式。但是,也不是说MVC就能解决一切问题。它就是所有的编程模式中最好的,它也会有它的一些不足。可是我们想想,世界上有什么东西又会是完美的呢?对于我们程序员而言,语言,IDE就是我们工具。只要用着顺手,能写出高效的代码就行了。管它谁人说那种语言好还是不好呢。废话说得有点多,我更新这个系列的文章是为了对自己学习的一个总结,并且分享给尚在学习阶段的兄弟。在园子里写得比我好的大牛很多。所以,欢迎指正错误与不足。
既然这是介绍MVC,那我们就来说说什么是MVC。传统的MVC其实是一种理念。旨在分离关注点。及把我们的系统按照不同的关注点分离开来。这样系统以各自的关注点分离开来。整个系统的可维护性,扩展性都会大大的提高。整个系统的层次结构也会更加的清晰。我们这里所谈的MVC其实是ASP.NET MVC。ASP.NET MVC其实是一种框架。以ASP.NET为平台的框架,也就是说,其实它其实还是运行在ASP.NET运行时上的,微软根据MVC的理念,在ASP.NET上封装了一套遵循了MVC理念的框架。我们的程序在理论上大部分的时候是在处理数据,而数据的处理方式是遵循特定行业的特定业务逻辑。以界面来展示数据。那么至少从这里我们可以抽象出一下几个关注点
- 界面
- 界面逻辑
- 业务逻辑
- 数据
在这个时刻,我们至少是可以总结出以上的关注点,遵循分离关注点的原则,我们至少要把这些关注点给分离开来。
在这里,我们是把数据和业务逻辑放在了一起。因为从领域编程的角度,数据也好,业务逻辑也好。他们都是属于某些特定领域的。比如金融领域,航空领域。所以这里他们是可以放在一起的,其中还会包括一些其他的,比如验证,属性标注等等。
我们看上面的一张图可以看到。关注点被分离。MVC的全称是Model-View-Controler。这里不正是我们关注的分离点吗
从这里我们可以总结以下的概念
视图:一个动态生成HTML页面的模板
模型:模型是描述程序设计人员感兴趣问题域的一些类,这些类通常封装存储在数据库中的数据,以及操作这些数据和执行特定域业务逻辑的代码。在ASP.NET MVC中,模型就像是一个使用了某个工具的数据访问层,包括实体框架。
控制器:一个协调视图和模型之间关系的特殊类。它响应用户输入,与模型进行对话,并决定呈现哪个视图。
我们现在来具体的解读他们之间的联系和各组件之间是怎么通讯和协作的,首先,我们来看View,View的作用很直接,就是现实用户的数据,获取用户的交互。也就是获取输入然后经过处理展现数据。上面一句话提到了“经过处理”,这里就又有了另外的一个问题,经过什么处理,什么东西来处理。其实就是我们的Controler。它获取用户的输入,经过自己的处理逻辑然后将数据处理操作交给Model进行处理。Model处理了业务逻辑,完成数据之后会有两种操作,一是由Controler处理界面逻辑,更新数据,然后View展现数据。第二种方式是处理完数据,由数据直接反应更改到View。这个时候是不经过Controler处理界面逻辑的,他们之间的关系是一个三角关系。可以从下面的一张图来看出他们之间的关系。
从上图我们可以看到M-V-C之间的通讯。这些都只是理论层面上的,从代码层面我们现在还没有看到代码的执行过程。下面我们来看一个最简单的MVC程序,执行的深层原理我们以后再讲,这一章我们只是看一个最简单的程序,看看MVC的执行过程。
首先,我们需要创建项目,打开VS,新建一个MVC空项目。这里顺便多说一句为什么是空项目。因为这样VS不会帮我们创建范例代码。如果不选择空程序,VS会帮我们创建一些范例代码来让我们了解MVC。
我们这里选择空模板,视图引擎我们选择Razor。视图引擎指的是我们操作界面的方式。比如怎么利用数据组合html。这里我们不用深究Razor,以后的章节会讲到Razor。测试项目我们这里暂时就不创建,因为我们单单就是一个示例程序,不需要那么复杂。创建项目之后我们可以看到如下的文件夹结构。
这里我们解释一下各个文件夹的用途
- App_Data:存放应用程序的数据文件,比如数据库文件,XML等等。
- App_Start:存放应用程序启动时需要的些类,这里是路由和路由注册的类(路由的概念以后会讲到)
- Controlers:Controler类的存放目录
- Models:Model的存放目录
- Views:View的存放目录
- Global.asax:全局处理,包含应用程序启动时需要执行的代码,这里是使用App_Start里面的一类来注册路由
- packages.config:NuGet包管理(暂时不需要了解,以后会讲到)
- web.config :这个大家都很熟悉了,网站的配置文件
按照一般的文件夹结构应该是还会有Content这个文件夹,至少在VS2010里是有这个文件件夹的,但是我这里的VS2012是没有这个文件夹的。这个文件夹存放的是网站的一般资源文件,比如js脚本,图片,CSS文件等等,如果没有我们可以手动添加一个,或者添加一些我们自己的文件夹。好了,项目我们是新建出来了,那么,我们该怎么开始呢?我们前面讲到了MVC实际就是Model-Controler-View。我们就从这里开始。首先我们新建一个Contrler,命名为HoneControler。具体的步骤是我们可以右键点击Controllers文件夹,添加Controller。
我们可以看到如下的界面,这里我们一切留空,除了命名。模板我们以后再来深入的了解。这里我们只是简单的看看MVC怎么执行的。点击确定之后我们可以看到如下的代码
namespace FirstMVC.Controllers { public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } } }
下一步我们修改生成的代码,我们把返回类型改为string,然后返回一个字符串
namespace FirstMVC.Controllers { public class HomeController : Controller { // // GET: /Home/ public string Index() { return "Welcome, MVC"; } } }
下面我们就可以直接启动项目了,我们可以看到路径是http://localhost:3222/之类的。我们在Index中返回的字符串直接输出到浏览器。这里我们就完成了一个最简单的MVC。但是这个例子是不够来解释我们MVC的整体执行过程的。我们在这个例子的基础上扩展来说明执行过程。这里,我们一步一步的来完成我们完成的数据读取的示例。
我们添加一个方法到我们的HomeController,方法名为ShowMessage。
public ActionResult ShowMessage() { return View(); }
这里需要先解释这几句代码,这里定义了一个返回值为ActionView的方法。ActionView是 一个抽象类型,是指操作(这里是ShowMessage方法)的结果,我们这里的结果就是返回一个View,也就是我们的视图。可以看到我们直接返回了View()。这里我们需要再解释一个概念-约定优先于配置
约定优先于配置:在以前的很多时候,我们在想要自定义应用程序中的一些行为时我们会使用配置文件,或者使用配置文件来制定应用程序中的一些特定行为。约定优先于配置是后来提出的一种概念。它的理念是大家一致遵守一种约定以取代配置对程序的特定行为的指定。这里我们的约定就是
- 控制器类以Controller结尾
- 在View文件夹下有一个与控制器同名的文件夹存在
- View文件夹下与Controller同名的文件夹下的视图(.cshtml-一种新的类型)的名称与控制器中Action(方法)名称相同。
所以,这里当我们直接Retrue View()的时候,解释引擎会直接找到View----Home----ShowMessage视图。所以下面,我们需要添加一个与ShowMessage方法对应的View。我们可以右键点击ShowMessage方法。选择添加视图。就会生成对应的视图。
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>ShowMessage</title> </head> <body> <div> <h1>This is ShowMesage Page!</h1> </div> </body> </html>
到这里我们的控制器和视图已经定义完成。打开浏览器输入http://localhost:3222/Home/ShowMessage我们可以看到我们在ShowMessage.CSHTML中的h1标签的内容。这里需要注意一点,URL的端口请于自己机器具体端口相关。好了,到了这里,我们要解释程序的执行过程了。
当URL进入通道后,运行时会感觉URL配置来获取Controller,Action和参数,URL路由的配置在RouteCongfig类中,代码如下
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
我们可以看到这里定义了路由规则url: "{controller}/{action}/{id}"不同的部件以“/”隔开。所以这里划分我们的URL,Home就是控制器的名称,ShowMessage就是Action的名称,参数我们这里没有。所以根据划分得到的URL,下面的执行步骤就是实例化HomeController调用ShowMessage方法,而ShowMessage方法返回的是ShowMessageView。所以,我们就可以在浏览器中看到ShowMessageView了。
在URL路由规则中,有一行代码defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },这里指定的是默认值,也就是当我们的URL没有指定Controller,Action,参数时的默认值。回到上面我们最开始的URLhttp://localhost:3222/。这里,我们没有指定Controller,Action。所以运行时会使用默认的路由配置来填充URL。这里就会指向http://localhost:3222/Home/Index。
我们使用了一个示例解释了MVC的执行过程,但是这里可以看到,我们的示例完全只使用了C和V,没有使用Model。所以,我们继续完善我们的示例,加入数据操作,在Model文件夹下新建一个实体类Person。
public class Person { private int _personId; public int PersonId { get { return _personId; } set { _personId = value; } } private string _firstName; public string FirstName { get { return _firstName; } set { _firstName = value; } } private string _lastName; public string LastName { get { return _lastName; } set { _lastName = value; } } public Person() { this.PersonId = 1; this.FirstName = "Edrick"; this.LastName = "Liu"; } }
在HomeController中添加一个新的Action,名称为ShowPerson,添加完Action之后添加对应的View,我们要使用这个View来显示Person的数据。
public class HomeController : Controller { // // GET: /Home/ public string Index() { return "Welcome, MVC"; } public ActionResult ShowMessage() { return View(); } public ActionResult ShowPerson() { Person person = new Person(); ViewBag.Person = person; return View(); } }
ShowPerson View代码如下
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>ShowPerson</title> </head> <body> <div> <p>@ViewBag.Person.PersonId</p> <p>@ViewBag.Person.FirstName</p> <p>@ViewBag.Person.LastName</p> </div> </body> </html>
在HomeController中,我们实例化了Person并且在实例化的时候插入了数据。并且把person装入ViewBag中以供视图使用。这里也就是使用Controller建立了View和Model的联系,但是这种联系是弱类型的。并不是直接建立在View和Model之间的。这里我们先只了解ViewBag实际是一个数据集合。以后会详细的解释ViewBag。
最后,在浏览器中键入地址http://localhost:3222/Home/ShowPerson 可以看到我们的person数据。这样我们就完成了一个典型的MVC程序。当然也是最简单的,我们先只需要了解什么是MVC,MVC是怎么执行的等等。以后的章节我们会详细解析每一个模块。