C#高级编程42章 MVC
42.1 ASP.NET MVC
路由机制 网络介绍链接
按照传统,在很多Web框架中(如经典的ASP、JSP、PHP、ASP.NET等之类的框架),URL代表的是磁盘上的物理文件。例如,当看到请求http://example.com/albums/list.aspx时,我们可以确定该站点目录结构中含有一个albums文件夹,并且在该文件夹下还有一个list.aspx文件。
URL和文件系统之间这种一一对应的关系并不适用于大部分基于MVC的Web框架,如ASP.NET MVC.一般来说,这些框架采用不同的方法将URL映射到某个类上的方法调用,而不是映射到磁盘上的物理文件。
另外,对于MVC应用程序,URL 请求到达的第一个组件是控制器而不是视图,而控制器是没有物理路径的
控制器方法不能重载
路由是从上到下的优先级的!所有
routes.MapRoute("showBlogRoute", "blog/post/{id}", new { controller =“CMS”,action = “Show”,id=“”});//如果参数2是POST,则匹配这一条显示这个控制器 routes.MapRoute("blogRoute", “blog/{action}/{id}", new { controller = "CMS", action = “Index", id = “”});//除名为POST的参数都显示这个方法 routes.MapRoute(“DefaultRoute”, // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller = "Home", action = "Index", id =“”} // 参数默认值 );
如上代码反过来!则不会匹配到"blog/post/{id}",这一条,直接匹配最后一条
Constraints属性是一个包含针对URL参数的验证规则的字典,就是说,它是用来限定每个参数的规则或Http请求的类型的
使用正则表达式来定义约束,比如下面的例子,如果希望以正确的格式(只允许数字值)指定年月日(正则式)
routes.MapRoute("simple", "archive/{year}/{month}/{day}", new{controller="blog",action="search",year="2009",month="10",day="10"}, Constraints: new{ year=@"\d{2}|\d{4}",//只能是两位或四位数字,这里的year对应到url里的year month=@"\d{1,2}",//只能使用一位或两位数字 day=@"\d{1,2}"//只能使用一位或两位数字 });
42.3创建控制器
42.3.2参数
一个参数
public ActionResult test(string name) { ViewBag.Message = "测试信息:" + name; //当打开http://localhost/Home/test?name=88832564时 name的值就等于88832564 return View();//<h3>@ViewBag.Message</h3>显示于ViewBag.Message的值 }
多个参数
public string test(int x ,int y)//取路由的参数值(id必须对应路由) { return "测试信息:" +(x * y);//url应该为:home/test?x=5&y=8 中间用:“&” }
以路由参数取值时可用
public ActionResult test(string id)//取路由的参数值(id必须对应路由) { ViewBag.Message = "测试信息:" + id; // 这里可以使用home/test?id=regfgfdg或/home/test/regfgfdg return View();//<h3>@ViewBag.Message</h3>显示于ViewBag.Message的值 }
42.3.3返回数据
public ActionResult test()//取路由的参数值(id必须对应路由) { //return Content("hello world","text/plain");//返回无格式正文字符串 //return JavaScript("<script>function foo {alert('foo');}</script>");//返回JavaScript脚本 //return Redirect("http://www.126.com");//跳转网站 重定向 //return RedirectToRoute(new { controller = "Home", action = "About" });//重定向路由 return File("~/Content/Images/timg.jpg", "image/jpg"); //返回文件对象 } public ActionResult JsonDemo() //反回JSON数据 { var m = new Menu { Id = 3, Text = "Grilled sausage with sauerkraut und potatoes", Price = 12.90, Category = "Main" }; return Json(m, JsonRequestBehavior.AllowGet); } //以下是menu类 using System.ComponentModel; using System.ComponentModel.DataAnnotations; public class Menu { public int Id { get; set; } [Required, StringLength(25)] public string Text { get; set; } [DisplayName("Price"), DisplayFormat(DataFormatString = "{0:C}")] public double Price { get; set; } [DataType(DataType.Date)] public DateTime Date { get; set; } [StringLength(10)] public string Category { get; set; } }
42.4.1 向视图传递数据
在控制器里用ViewBag.data设置在视图里用@ViewBag.data取得,可以理解决ViewBag是一个类或数组,视图通过其进行设置
42.4.2 Razor语法
相关的语法 链接 以下是视图里的相关写法 在ASPX语法中使用<%:ViewBag.MyData %>
@{ //这里可以插入代码 @ViewBag.Message; //这里输出相关字符串 }
42.4.3强类型视图
public ActionResult test()// { var menus = new List<Menu> { new Menu { Id=1, Text="Schweinsbraten mit Knödel und Sauerkraut",Price=6.9, Category="Main" }, new Menu { Id=2, Text="Erdäpfelgulasch mit Tofu und Gebäck",Price=6.9, Category="Vegetarian" }, new Menu { Id=3, Text="Tiroler Bauerngröst'l mit Spiegelei und Krautsalat", Price=6.9, Category="Main" } }; return View(menus);//传递menus类给视图 }
using声明类结构,在视内用model关键之际定义模型,此模型的类型为IEnumerable<Menu>
@using MyWeb.Models @model IEnumerable<Menu> <div> <ul> @foreach (var item in Model) { <li>@item.Text</li> } </ul> </div>
42.4.4布局
如果视图不显示布局则使用用
@{ Layout = null; }
1、默认布局
_ViewStart.cshtml为页面包含全部视图的默认配置 Layout属性设定为共享布局页
布局页:@ReanderBody()为主体 @ViewBag.Title为标提
2、使用分页
布局页写入:@RenderSection("PageNavigation", required: false) 视图页:在代码域中写入HTML分页代码 @section PageNavigation { <div>Navigation defined from the view</div> }
42.4.5部分视图
部分视图示例要使用的结构类存放于Models
public class EventsAndMenus { private IEnumerable<Event> events = null; public IEnumerable<Event> Events { get { return events ?? (events = new List<Event>() { new Event { Id=1, Text="Formula 1 G.P. Abu Dhabi, Yas Marina",Day=new DateTime(2014, 10, 26) }, new Event { Id=2, Text="Formula 1 G.P. USA, Austin",Day = new DateTime(2014, 11, 9) }, new Event { Id=3, Text="Formula 1 G.P. Brasil, Sao Paulo",Day = new DateTime(2014, 11, 30) } } ); } } private List<Menu> menus = null; public IEnumerable<Menu> Menus { get { return menus ?? (menus = new List<Menu>() { new Menu { Id=1, Text="Baby Back Barbecue Ribs", Price=16.9, Category="Main" }, new Menu { Id=2, Text="Chicken and Brown Rice Piaf", Price=12.9, Category="Main" }, new Menu { Id=3, Text="Chicken Miso Soup with Shiitake Mushrooms", Price=6.9, Category="Soup" } }); } } }
1、使用服务器端代码中的部分视图
下面的方法是把结构类数据通过控制器发给视图,视图通把结构类数据发给部分视图然后显示出来
// 控制器 public ActionResult test()// { return View(new EventsAndMenus()); } //视图,把结构体Model.Events传给viewtes部分视图 @model MyWeb.Models.EventsAndMenus @{ ViewBag.Title = "UseAPartialView21"; } <h2>UseAPartialView1</h2> <div>this is the main view</div> <div> @Html.Partial("viewtest", Model.Events) </div> //部分视图中:viewtest.cshtml @using MyWeb.Models @model IEnumerable<Event> <h2> @ViewBag.EventsTitle </h2> <table> @foreach (var item in Model) { <tr> <td>@item.Day.ToShortDateString()</td> <td>@item.Text</td> </tr> } </table>
2、从控制器中返回部分视图
以下是通过视图调用控制器方法的返回Html代码然后显示在视图里
//视图页:test1.cshtml,调用Viewtest方法的结果返回Html @Html.Action("viewtest") //控制器: public ActionResult viewtest() { ViewBag.EventsTitle = "Live Events"; return PartialView(new EventsAndMenus().Events); } //部分视图:viewtest.cshtml @using MyWeb.Models @model IEnumerable<Event> <h2> @ViewBag.EventsTitle </h2> <table> @foreach (var item in Model) { <tr> <td>@item.Day.ToShortDateString()</td> <td>@item.Text</td> </tr> } </table>
3、在JQuery中调用部分视图
<script>
$(function () {
$("#getEvents").click(function () {
$("#events").load("/ViewsDemo/ShowEvents");
});
});
</script>
<button id="getEvents">Get Events</button>
<div id="events"></div>
42.5 从客户端提交数据
45.5.1模型绑定:方法是从视图提交表单的处理方法!直接模型绑定到Menu类中,然后返回视图
视图页:CreateMenu.cshtml @{ ViewBag.Title = "Create Menu"; } <h2>Create Menu</h2> <form action="/SubmitData/CreateMenu" method="post"> <fieldset> <legend>Menu1</legend> <div>Id:</div> <input name="id" /> <div>Text:</div> <input name="text" /> <div>Price:</div> <input name="price" /> <div>Category:</div> <input name="category" /> <div></div> <button type="submit">Submit</button> </fieldset> </form> 控制器: [HttpPost]//这里限制方法为HttpPost请求 public ActionResult CreateMenu(Menu m) { //var m = new Menu { Id = id, Text = text, Price = price };
ViewBag.Info = string.Format( "menu created: {0}, Price: {1}, category: {2}", m.Text, m.Price, m.Category); return View("Index"); } 视图页:index.cshtml <h2>Index</h2>
上面代码可以改为:(以下方法如果有一些不应该更新的属性就不应该使用UpdateModel方法,否则用户可以恶意修改请求更新这些属性)
[HttpPost]//这里限制方法为HttpPost请求 public ActionResult CreateMenu2() { var m = new Menu(); UpdateModel<Menu>(m);//使用来自控制器的当前值提供程序的值更新指定的模型实例 ViewBag.Info = string.Format( "menu created: {0}, Price: {1}, category: {2}", m.Text, m.Price, m.Category); return View("Index"); } 视图中改为:<form action="/SubmitData/CreateMenu2" method="post">
42.5.2注释与验证
在模型绑定中通过
if (ModelState.IsValid)//获取包含模型状态和模型绑定验证状态的模型状态字典对象。
来判定模型状态 模型状态通过[StringLength(10)]来验证
当模型为工具生成时,则在别外新增一个MenuMetadata属性与模型相同的有验证的!!
public class MenuMetadata { public int Id { get; set; } [Required, StringLength(25)] public string Text { get; set; } [DisplayName("Price"), DisplayFormat(DataFormatString = "{0:C}")] public double Price { get; set; } [DataType(DataType.Date)] public DateTime Date { get; set; } [StringLength(10)] public string Category { get; set; } } //在用工具生的模型的类前面添加 [MetadataType(typeof(ManuMatadata))] public partial class Menu { }
42.6 HTML Helper
帮助文链接 相关说明请查网上 下面为常用的一些代码
- Html.Actionlink(显示名称,方法)链接到操作方法
- Html.Action(方法名)返回方法的结果的HTML代码
- Html.Partial(部分视图页,数据)把数据传递到部分视图页显示并返回
- Html.DisplayName(显示字符串)
- Html.Display('模型的名称') 显示模型中相关名称的数据
- Html.TextBox("Html控件id","值",new{required="required",maxlength=15,@class="CssDemo"}) 定义TextBox控件
- Html.EditorFor(m=>m)返回表达式中每个属性对应的Html元素
- Html.EditorForModel()返回模型中返有属性对应的Html元素
- @using Html.BeginForm("方法名", "控制器名", FormMethod.Post) {这里为表单,<input type="submit" name="BtnSubmit" value="Login" />} 把表单发送给控制器
- Html.RenderAction(方法) ,这里理解只返回方法的结果的意思
- Html.RenderPartial("部分视图",模型) 可以理解这里是返回视图,
42.6.4创建列表 (下拉列表)
public static class SelectListItemsExtensions //自定义扩展方法 { public static IEnumerable<SelectListItem> ToSelectListItems(this IDictionary<int, string> dict, int selectedId) { return dict.Select(item => new SelectListItem { Selected = item.Key == selectedId, Text = item.Value, Value = item.Key.ToString() } ); } }
public ActionResult test()//控制器方法 { var cars = new Dictionary<int, string>(); cars.Add(1, "Red Bull Racing"); cars.Add(2, "Mclaren"); cars.Add(3, "sgvd"); return View(cars.ToSelectListItems(3)); } //视图显示 @model IEnumerable<SelectListItem> @Html.DropDownList("carslist", Model)
42.6.5强类型化的helper
从控制器传到视图时 相关的链接
@model MyWeb.Models.Menu
@Html.TextBoxFor(m=>m.Text)
24.6.7自定义helper
视图页:通过DispayDay(Model.Date)把模型传致上面的方法中
@model MyWed.Models.Menu @helper DispayDay(DateTime day) { if (day < DateTime.Parse("2018-1-16 2:28:22")) { <span>历史日期</span> } @String.Format("{0:d}", day); } @Html.DisplayFor(m => m.Text) @DispayDay(Model.Date)
24.6.8 模板
视图中通过DisplayForModel()来显示模板或DisplayFor(m => m.Date)
在视图下面新建DisplayTemplates文件夹,或在共享文件Shared下面新建DisplayTemplates方件夹,在DisplayTemplates方件夹新建类型模板,如Date类型:在目录下新建Date.cshtml,内容如下:
<div class="markRed"> @string.Format("{0:D}", Model) </div>
Content里面Site.css更改样式:
.markRed{ color:#ff0000; }
而模型里面
[DataType(DataType.Date)]
注:因EF的问题,转到学习"7天玩转MVC"