之前一篇写一半发现版本太老了,是基于mvc2的。
两本参考书编写的顺序各方面都不太一样。决定重新写一篇。
我这篇文章基于mvc5,vs2015
参考书:Will保哥的ASP.NET MVC4开发指南
一、创建一个项目
目录图解
1.从路由开始
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace MvcGuestbook { 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 } ); } } }
IgnoreRoute:目的是设定 *.axd等格式的文件不要通过ASP.NET运行,意义在于让webform和mvc文件可以在同一个平台独立运行互不干扰。
MapRoute:MapRoute方法是定义mvc网址路由的最主要方式。
这一条路由定义了三个参数:
name:路由名称
uri:设置网址路径如何对应到控制器、动作与路由值
default:设置了{Controller}{Action}{Id}这三个路由参数的默认值
如下图网址
其中{controller}是home,{action}是About。ASP.NET MVC会先进入Controllers目录找home控制器(homeController.cs文档),再找About公开方法,这个公开方法就是MVC的action,也是实际运行网页程序的公开入口。
如在浏览器中输入 http://localhost/ 想取得首页,由于网址路径部分没有任何属性,就会使用MapRoute的第三个参数(defaults)所设置的默认值来代替。因此网站会从Controllers目录查找Home控制器再查找Index这个公开方法,再进一步运行ASP.NET MVC。
我们来看一下HomeController的属性:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcGuestbook.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } } }
顺带补充controller类的规范:
1)必须用“Controller”结尾。
2)继承Controller基类(或实现IController接口)。
3)类中必须包含数个返回值为“ActionResult”的公开方法,这些方法在ASP.NET MVC中被称为Action。
可以看到,在上述代码Index行为中,第一行的ViewBag.Message是一个动态型(daynamic)的对象,因此可以放进任意型别的数据进去,这些属性和值都可以在ASP.NET MVC的View中读取。
ViewBag.Message = "Your application description page.";
我们查看HomeController中Index的视图
我们会发现Index.cshtml并不是一个完整的HTML页面,我们来看看主版面设置在哪里。
打开项目目录,我们会发现Views目录下有一个_ViewStart.cshtml文件
打开这个文件后里面只有三行代码。其中定义了Layout属性,并指向了"~/Views/Shared/_Layout.cshtml",默认所有View文件都会被装入该主板页面。打开_Layout.cshtml文件,我们不难发现里面的代码才是完整的HTML结构。
至于我们在之前的Controller中看到ViewBag.Message被设置了一串字符串,到了Index.cshtml检视(View)就可以通过以下语法读出并显示于网页属性中。
@ViewBag.message
另外Index.cshtml页最上方设置了一组ViewBag.Title属性,这里定义的属性值也会被自动传入同一个View和以及默认的_Layout.cshtml主版页面
2.创建数据模型
step1
step2
step3 定义出一个留言板所需的数据模型
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MvcGuestbook.Models { public class Guestbook { public int Id { get; set; } public string 姓名 { get; set; } public string Email { get; set; } public string 内容 { get; set; } } }
step4 生成一次解决方案确定没有问题
3.创建控制器、动作与检视
step1 我们这里不选择直接建空的控制器,我们选择利用基架(Scaffold)
step2
step3
其实此时已经完成,我们只需要按f5开始调试就可以了。
输入网址,进行试验
我们这个网站的页面功能还请大佬们自行测试一下,虽然我们没自行写代码但是程序确实已经完成了,mvc5的功能很是强大。
4.查看数据库属性
上述网站调试中我输入过一组数据,这组数据肯定是被存到哪里了,那么我们来看下这些数据被放到哪里去了。
我们点击解决方案资源管理器下面的显示所有文件按钮,就会显示出隐藏的文件,如下图。
我们点击那个按键后,App_Data下出现了一个 .mdf文件,这是一个数据库文档。
其中,项目模板会先在web.config文档中创建一个DefaultConnection连接字符串。
能在web.config中看到连接字符串演示
<connectionStrings> <add name="DefaultConnection" connectionString="Data Source=(LocalDb)MSSQLLocalDB;AttachDbFilename=|DataDirectory|aspnet-MvcGuestbook-20170327110627.mdf;Initial Catalog=aspnet-MvcGuestbook-20170327110627;Integrated Security=True" providerName="System.Data.SqlClient" /> <add name="MvcGuestbookContext" connectionString="Data Source=(localdb)MSSQLLocalDB; Initial Catalog=MvcGuestbookContext-20170327151548; Integrated Security=True; MultipleActiveResultSets=True; AttachDbFilename=|DataDirectory|MvcGuestbookContext-20170327151548.mdf" providerName="System.Data.SqlClient" /> </connectionStrings>
vs2015中,我们可以直接选择 MvcGuestbookContext-(日期).mdf 个文档,打开后可以自由的浏览或变更数据库架构。
二、认识自动生成的代码
之前代码均由mvc5自动生成,接下来让我们来了解下这些通过工具自动帮我们逐步生成的代码。
1.了解列表Index()动作
打开Controller目录下的GuestbookController.cs。
其中第一行定义了MvcGuestbookContext类的私有变量,也就是我们的数据内容类。这整个Controller类都会用db这个变量对数据库进行访问
private MvcGuestbookContext db = new MvcGuestbookContext();
Index的公开程序代码很简单,只有一行。
其中View()是来自Controller基类的一个辅助方法,该辅助方法有8个重载,其中第3个多载是传入一个model参数,此参数的对象数据会传给View使用。
这里我们传入了db.Guestbooks.ToList(),代表着把所有Guestbooks回传的所有数据全部传入View中,让View里的程序去使用。接着,我们切换到Index动作方法(Action Method)对应的视图的检视页面(View Page)。
2.了解列表页面的Index检视
在Views/Guestbook/Index.cshtml 检视页面的第一行,里面有一个@model声明,后面便是一个IEnumerable类,IEnumerable是Guestbook的集合对象。表示View会以用@model声明的类别为“主要模板”,在View里面的程序代码也会参考到该类别使用
接下来这段 ViewBag.Title的设置在本页中没有用到,而是要传给主版页面(Layout Page)用的,显示在HTML的Title卷标中
@{ ViewBag.Title = "Index"; }
下面一段代码是用来创建ASP.NET MVC链接的,链接显示为"Create New",而该链接将会链接到这页控制器的Create动作(Action),超链接的输出则由ASP.NET MVC负责。
<p> @Html.ActionLink("Create New", "Create") </p>
由于是列表页面,有<table>标签,会将页面输出成表格
<tr> <th> @Html.DisplayNameFor(model => model.姓名) </th> <th> @Html.DisplayNameFor(model => model.Email) </th> <th> @Html.DisplayNameFor(model => model.内容) </th> <th></th> </tr>
@Html.DisplayNameFor()的主要用途是输出特定字段的显示名称,传入的参数是一个Lambda Expression表示法,该表示法里传入的model变量代表的正是我们在View里第一页设置的@model型别,所以我们可以在挑选字段的时候利用vs的Intellisense来帮助选择。@Html.DisplayNameFor()会默认直接输出属性名称。最后输出html页面如下:
<tr> <th> 姓名 </th> <th> Email </th> <th> 内容 </th> <th></th> </tr>
若要输出的显示名称和属性名称不同,则必须更改Guestbook模型类别的定义,在特定一个属性(Property)上加上一个System.ComponentModel命名空间下的DisplayName属性(Attribute),在我们这里将Email字段显示名称更改成“电子邮件地址”,如下:
修改完就可以直接f5运行网站。可以看到Email字段所输出的域名直接变成了“电子邮件地址”。显示的html代码如下:
<tr> <th> 姓名 </th> <th> Email </th> <th> 电子邮件地址 </th> <th></th> </tr>
View最后一段Code是一个数据来自Model对象的foreach循环。这里的Model对象是每个View都有的属性,代表的就是从Controller传过来的数据,而这个Model对象本身就是泛型类,也就是说这个Model对象的类会完全等同于你在View最上方用@model声明的类。
代码如下,我们通过循环取出Model里的每条数据,每条数据的类型刚好是MvcGuestbook.Models.Guestbook的模型类型。
@foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.姓名) </td> <td> @Html.DisplayFor(modelItem => item.Email) </td> <td> @Html.DisplayFor(modelItem => item.内容) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.Id }) | @Html.ActionLink("Details", "Details", new { id=item.Id }) | @Html.ActionLink("Delete", "Delete", new { id=item.Id }) </td> </tr> }
刚刚是@Html.DisplayNameFor辅助方法,现在看到的是@Html.DisplayFor辅助方法,这段code被包含在@foreach循环中,所以传入@Html.DisplayFor的模型数据将会是循环内单条MvcGuestbook.Models.Guestbook模型的数据。
上述代码最后三行是edit,details,delete的链接,@Html.ActionLink()的用途是用来输出超链接的。
@Html.ActionLink("Edit", "Edit", new { id=item.Id }),第一个"Edit"是链接显示的文字,第二个"Edit"是链接的目的Action名称,第三个是代表路由参数名称id。第三个代表路由参数名称的id会在mvc在输出超链接时,加上要传给下一页的路由参数,方便ASP.NET MVC知道你除了传入{Controller},与{Action}路由参数外,顺便给予{id}路由值。这里的{id}稍后将提及,在Edit动作时会提及。
我们来了解下创建窗体窗口的Create动作。
切回到GuestbookControllers类里观察创建信息功能的动作的Action。控制器里有两个同名的Create方法,一个是给Http Get用的一个是给Http Post用的。
值得一提的是,第二个Create有特别套用的一个HttpPost属性(Attribute),该属性告知ASP.NET框架此Action只接受Http Post过来的信息,这个属性又有另一个专有名词叫动作过滤器(Action Filter)或者动作选择器(Action Selector)
// GET: Guestbooks/Create public ActionResult Create() { return View(); }
// POST: Guestbooks/Create // 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关 // 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create([Bind(Include = "Id,姓名,Email,内容")] Guestbook guestbook) { if (ModelState.IsValid) { db.Guestbooks.Add(guestbook); db.SaveChanges(); return RedirectToAction("Index"); } return View(guestbook); }
首先我们进入http://localhost:63471/Guestbooks/Index页面之后首先Http执行的方法一定是Get,因此第一个Create()动作会首先被ASP.NET MVC选中运行,并显示默认的Create检视页面(View Page)。
切换至Create检视页面。
了解创建窗体的Create 检视页面
@model MvcGuestbook.Models.Guestbook
开头依旧来了个@model声明,声明此页面以MvcGuestbook.Models.Guestbook为主要模型
接着出现了一个ASP.NET MVC的窗体声明与窗体内Html声明,代码如下:
@using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Guestbook</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.姓名, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.姓名, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.姓名, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Email, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Email, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.内容, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.内容, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.内容, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> }
这里我们使用的是Html.BeginForm()辅助方法,该方法会输出<form>标签,而且用using包裹起来,如此便可以在using代码结束时让ASP.NET帮你补上<form>标签,而且必须以using包起来,这样就可以在using结束代码最后退出的时候让ASP.NET MVC帮补上</form>,以本页为例,最后输出的代码就是:
<form action="/Guestbook/Create" method="post"> ... </form>
@Html.ValidationSummary(true, "", new { @class = "text-danger" })是用来显示表单域发生验证失败时,显示的错误消息。
在这个创建信息的窗体中一共有三个字段,你会发现这三个字段的定义都差不多。
<div class="form-group"> @Html.LabelFor(model => model.姓名, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.姓名, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.姓名, "", new { @class = "text-danger" }) </div> </div>
@Html.LabelFor()用来显示特定字段的显示名称。和@Html.DisplayNameFor()类似,而用@Html.DisplayNameFor()只会输出域名,使用@Html.LabelFor()会输出包含<label>标签的域名。
@Html.LabelFor()和@Html.DisplayNameFor()的输出比较
Razor语法 | Html显示结果 |
@Html.DisplayNameFor(Model=>model.Email) | 电子邮件地址 |
@Html.LabelFor(Model=>model.Email) | <Label for="Email">电子邮件地址<Label> |
ASP.NET中主要使用@Html.EditorFor来输出表单域,以此字段为例,输出的Html代码如下,这里出现的class属性是默认输出的,可以设计css样式来改变该输出字段的样式:
<input class="text-box single-line" name="姓名" type="text" value="" />
最后一个@Html.ValidationMessageFor是用来显示字段验证的错误消息,不过到目前为止我们没有对Create页面进行任何字段验证的设置。