AspNet Mvc一些总结
RestaurantReview.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text.RegularExpressions; using System.Web; using System.Web.Mvc; using Antlr.Runtime; namespace Kwin.AspNetMvc.OdeToFood.Models { /// <summary> /// /// </summary> /// <remarks> /// 1.Bind特性用于锁定可以进行模型绑定的属性, /// [Bind(Include = "City, Country, Rating")] ,白名单的形式,推荐 /// [Bind(Exclude = "Id, Name")] ,黑名单的形式 /// /// 可以在三个位置设置: /// 1.Global.asax.cs 类型列表中设置绑定规则 /// 2.在类定义上<see cref="RestaurantReview"/> /// 3.在方法参数上 /// -------------------- /// 2.数据验证方法: /// http://developer.51cto.com/art/201404/435126.htm /// http://www.cnblogs.com/zhangkai2237/archive/2012/12/12/2814825.html /// /// 1).数据验证,方法:实现IDataErrorInfo,演示:<see cref="RestaurantReview"/> /// 成员函数在UpadtaModel时会被调用,(永远不要相信客户端,服务器这边再检查一遍) /// -------------------- /// 2).数据验证,方法:继承ValidationAttribute /// 演示MaxWordsAttribute<see cref="MaxWordsAttribute"/> /// 这个特性和普通数据注解的区别是普通数据注解仅仅只能验证Model的一个属性, /// 需要注意的是,自定义的数据注解不支持客户端验证,所有的数据需要提交之后再服务端验证,所以如果要同时实现客户端验证需要自己写js验证 /// /// 3).数据验证,方法:IValidatableObject /// 这个接口是为了实现Model的自验证(self-validating)的,是asp.net mvc3 新增的验证特性。 /// 这个特性和普通数据注解的区别是普通数据注解仅仅只能验证Model的一个属性, /// 而实现了IValidatableObject接口的自验证则在Model的级别来验证 /// /// 4).人工验证 /// </remarks> //[Bind(Include = "Id, Name, City, Country, Rating")] public class RestaurantReview : IValidatableObject // ,IDataErrorInfo { Dictionary<string, string> _errorDictionary = new Dictionary<string, string>(); private int rating; public int Id { get; set; } [Required(ErrorMessage = "{0}不能为空!LoL")] [Display(Name = "名称")] [Remote("CheckNameIsExisted", "Account", HttpMethod = "POST",ErrorMessage = "该用户名已经被使用")] [MaxWords(10,ErrorMessage = "{0}你输入的太多了")] //[StringLength(6)] public string Name { get; set; } [Required] [Display(Name = "城市")] [DisplayFormat(NullDisplayText = "N/A")] public string City { get; set; } [Required] [Display(Name = "国家")] [DisplayFormat(NullDisplayText = "N/A")] public string Country { get; set; } [Required(AllowEmptyStrings = true)] [Display(Name = "等级")] [Range(typeof(int),"1","10",ErrorMessage = "{0}必须是{1}和{2}之间的数字")] public int Rating { get { return this.rating; } set { bool IsNum = Regex.IsMatch(Convert.ToString(value), @"^\d+$"); if (!IsNum) { _errorDictionary.Add("Rating", "等级必须是数字"); } this.rating = value; } } #region IDataErrorInfo Members /// <summary> /// 获取一个单一的错误消息,指示对象实例中的错误 /// </summary> public string Error { get { return string.Empty; //这里没有实现,返回值一个空的错误消息 } } /// <summary> /// 用来接收参数和返回一个错误消息指示属性中的错误 /// </summary> /// <param name="columnName"></param> /// <returns></returns> /// <remarks> /// 服务端再次进行数据验证(原则:永远不要相信客户端)。 /// /UpadtaModel(model)时会调用该方法, /// 如果返回值不为string.Empty,该返回值会被添加到ModelState.Values属性中,并将 /// ModelState.IsValid设置为false。 /// </remarks> public string this[string columnName] { get { if (_errorDictionary.ContainsKey(columnName)) { return _errorDictionary[columnName]; } return string.Empty; } } #endregion #region IValidatableObject /// <summary> /// /// </summary> /// <param name="validationContext"></param> /// <returns>返回类型是 IEnumerable(一种迭代器接口类型)</returns> public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (Rating < 2 && Name.ToLower().Contains("hacker")) { yield return new ValidationResult("sorry,Mr.Hacker, you can't do this", new[] { "Name" }); } } #endregion } }
[HttpPost]//Controller中也可以直接接受Post的请求,可以不需要[Httppost]注释: public ActionResult CheckNameIsExisted(string name) { bool result = name != "admin"; return Json(result, JsonRequestBehavior.AllowGet); }
ReviewsController.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Configuration; using System.Data.Objects.DataClasses; using System.Linq; using System.Security.Cryptography; using System.Web; using System.Web.Mvc; using System.Web.Mvc.Html; using Kwin.AspNetMvc.OdeToFood.Models; using PagedList; namespace Kwin.AspNetMvc.OdeToFood.Controllers { public class ReviewsController : Controller { #region 测试数据, private static bool _hasInitilizeDatas = false; private static List<RestaurantReview> _restaurantReviews = new List<RestaurantReview>() { new RestaurantReview() { Id = 1, Name = "Cidde ha", City = "shanghai", Country = "China", Rating = 10 }, new RestaurantReview() { Id = 2, Name = "张氏", City = "shanghai", Country = "中国", Rating = 8 }, new RestaurantReview() { Id = 3, Name = "Bill", City = "shanghai", Country = "UK", Rating = 6 }, new RestaurantReview() { Id = 4, Name = "Stevien Jason", City = "shanghai", Country = "英国", Rating = 7 } }; #endregion public ReviewsController() { if (!_hasInitilizeDatas) { InitilizeDatas(); _hasInitilizeDatas = true; } } /// <summary> /// 模拟数据 /// </summary> private static void InitilizeDatas() { Random random = new Random(); for (int i = 1000; i < 2100; i++) { RestaurantReview restaurantReview = new RestaurantReview() { Id = i, Name = string.Format("老字号餐馆{0}", Convert.ToString(i)), //City = "N/A", //Country = "N/A", Rating = random.Next(1, 10) }; _restaurantReviews.Add(restaurantReview); } } public static bool HasInitilizeDatas { get { return _hasInitilizeDatas; } set { _hasInitilizeDatas = value; } } public ActionResult Index(string searchKey = null, int currentPage = 1) { int pageSize = Convert.ToInt32(ConfigurationManager.AppSettings["pageSzie"]); var model = _restaurantReviews.Where( r => searchKey == null || r.Name.ToLower().Contains(searchKey.ToLower().Trim())) .OrderByDescending(r => r.Rating) .Select(r => new RestaurantReview() { City = r.City, Country = r.Country, Id = r.Id, Name = r.Name, Rating = r.Rating }) .ToPagedList(currentPage, pageSize); //局限:使用ToPagedList后不能用@Html.DisplayNameFor(model => model.Name) //.ToList(); if (Request.IsAjaxRequest()) { System.Threading.Thread.Sleep(1000 * 1);//模拟处理数据需要的时间 //return View(model)会返回整个页面,所以返回部分视图。 return PartialView("_RestaurantPatialView", model); } return View(model); } // // GET: /Reviews/Details/5 /// <summary> ///关于使用System.Web.Mvc.Ajax的说明: /// Controller的Action方法: /// (1)当显式添加[HttpPost],传给System.Web.Mvc.Ajax的AjaxOptions()的HttpMethod只能为 "post", /// (2)当显式添加[HttpGet],传给System.Web.Mvc.Ajax的AjaxOptions()的HttpMethod只能为 "get", /// (3) 当都没有显式添加[HttpPost]和[HttpGet],传给System.Web.Mvc.Ajax的AjaxOptions()的HttpMethod可以为 "get"也可以为"post", /// </summary> /// <param name="id"></param> /// <returns></returns> public ActionResult Details(int id=1) { var model = (from r in _restaurantReviews where r.Id == id select r).FirstOrDefault(); if (Request.IsAjaxRequest()) { return PartialView("_RestaurantDetails", model); } return View(model); } // // GET: /Reviews/Create public ActionResult Create() { return View(); } // // POST: /Reviews/Create #region FormColletion //[HttpPost] //[ValidateAntiForgeryToken] //public ActionResult Create(FormCollection collection) //{ // try // { // // Add insert logic here // if (ModelState.IsValid) // { // var model = new RestaurantReview(); // TryUpdateModel(model); // _restaurantReviews.Add(model); // } // return RedirectToAction("Index"); // } // catch // { // return View(); // } //} #endregion #region 使用ViewModel实体 /// <summary> /// /// </summary> /// <param name="model"></param> /// <returns></returns> /// <remarks> /// ValidateAntiForgeryToken特性: /// 与View中的@Html.AntiForgeryToken()一起使用, /// 防止CSRF攻击 /// </remarks> [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(RestaurantReview model) { try { if (ModelState.IsValid) { _restaurantReviews.Add(model); } return RedirectToAction("Index", "Reviews"); } catch (Exception) { return View(); } } #endregion // // GET: /Reviews/Edit/5 public ActionResult Edit(int id) { return View(); } // // POST: /Reviews/Edit/5 #region 使用FormCollection //[HttpPost] //[ValidateAntiForgeryToken] //与View中的@Html.AntiForgeryToken()一起使用 //public ActionResult Edit(int id, FormCollection collection) //{ // try // { // if (ModelState.IsValid) // { // //Add update logic here // var model = (from r in _restaurantReviews // where r.Id == id // select r).First(); // TryUpdateModel(model); // } // return RedirectToAction("Index"); // } // catch // { // return View(); // } //} #endregion #region 使用ViewModel实体 /// <summary> /// /// </summary> /// <param name="model"></param> /// <returns></returns> /// <remarks> /// 1.ValidateAntiForgeryToken特性: /// 与View中的@Html.AntiForgeryToken()一起使用, /// 防止CSRF攻击 /// ///2. Bind(Include="....")是用来解决Mass Assignment漏洞 /// Mass Assignment漏洞演示: /// 去掉Bind(Include="....") /// public ActionResult Edit(RestaurantReview model) /// /// 去掉Edit.cshtml中的某个字段如:Name的编辑标签(即:让用户无法修改Name字段) /// <div class="editor-field"> /// @Html.EditorFor(model => model.Name) /// @Html.ValidationMessageFor(model => model.Name) /// </div> /// /// 输入如下连接: /// Edit方法路由后面添加参数?Name=hack(Name会自动绑定到模型绑定类型RestaurantReview中的Name属性), /// 即: /// http://localhost:3951/Reviews/Edit/1003?Name=hack /// /// 经测试无上面的Mass Assignment,可能新aspnet mvc已经修复该bug /// </remarks> [HttpPost] [ValidateAntiForgeryToken] //public ActionResult Edit([Bind(Include = "Id, Name,City, Country, Rating")]RestaurantReview model) public ActionResult Edit(RestaurantReview model) { try { if (ModelState.IsValid) { var restaurentView = (from r in _restaurantReviews where r.Id == model.Id select r).FirstOrDefault(); TryUpdateModel(restaurentView); return RedirectToAction("Index"); } return View(); } catch (Exception ex) { ModelState.AddModelError("", ex.Message); //给View返回一个错误消息,最终交给@Html.ValidationSummary显示。 return View(); } } #endregion // // GET: /Reviews/Delete/5 public ActionResult Delete(int id) { var models = from restaurantReview in _restaurantReviews where restaurantReview.Id == id select restaurantReview; var model = models.First(); //var model = _restaurantReviews.SingleOrDefault(); return View(model); } // // POST: /Reviews/Delete/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Delete(int id, FormCollection collection) { try { // Add delete logic here var model = (from r in _restaurantReviews where r.Id == id select r).First(); _restaurantReviews.Remove(model); return RedirectToAction("Index"); } catch { return View(); } } [HttpPost]//Controller中也可以直接接受Post的请求,可以不需要[Httppost]注释: public ActionResult CheckNameIsExisted(string name) { bool result = name != "admin"; return Json(result, JsonRequestBehavior.AllowGet); } [ChildActionOnly] public ActionResult BestReview() { var starRestaurent = from r in _restaurantReviews where r.Rating >= 9 orderby r.Rating descending select r; return PartialView("_BestReview", starRestaurent.Take(5)); } /// <summary> /// /// </summary> /// <param name="term"></param> /// <returns> /// //http://localhost:3951/Reviews/autocompleted?term=老 //返回JSON,如下格式: // [ // {"label":"老字号餐馆1000"},{"label":"老字号餐馆1001"},{"label":"老字号餐馆1002"}, // {"label":"老字号餐馆1003"},{"label":"老字号餐馆1004"},{"label":"老字号餐馆1005"}, // {"label":"老字号餐馆1006"},{"label":"老字号餐馆1007"},{"label":"老字号餐馆1008"},{"label":"老字号餐馆1009"} // ] /// </returns> public ActionResult AutoCompleted(string term) { var model = _restaurantReviews.Where(r=>r.Name.ToLower().Contains(term.ToLower().Trim())) .Take(10) .Select(r=> new { label = r.Name //匿名对象的字段名必须是label,因为ui.item.label }); //serialize model into JSON format return Json(model,JsonRequestBehavior.AllowGet); } } }
@*@model IEnumerable<Kwin.AspNetMvc.OdeToFood.Models.RestaurantReview>*@ @model IPagedList<RestaurantReview> @{ ViewBag.Title = "我的推荐:餐厅系列"; ViewBag.Message = "美味不断,吃遍填下"; } @section mytile{ <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>@ViewBag.Title</h1><br/> <h2>@ViewBag.Message</h2> </hgroup> <p> </p> </div> </section> } @* -----------1.不用Ajax--------------------------- *@ @*<form method="post"> <input type="search" name="searchKey"/> <input type="submit" value="按名称搜索"/> </form>*@ @* -----------2.System.Web.Mvc.Ajax--------------------------- 不用手工为Form添加属性标签,MVC框架自己添加了, Ajax.BeginForm生成的标签如下: <form id="form0" method="post" data-ajax-url="/Reviews" data-ajax-update="#restaurantList" data-ajax-mode="replace" data-ajax-method="post" data-ajax-loading-duration="2000" data-ajax-loading="#loding" data-ajax="true" action="/Reviews" novalidate="novalidate"> ------------------------------------------------------------------*@ @*@using (Ajax.BeginForm( new AjaxOptions() { HttpMethod = "post", Url = @Url.Action("Index","Reviews"), InsertionMode = InsertionMode.Replace, UpdateTargetId = "restaurantList", LoadingElementId = "loding", LoadingElementDuration = 2000 })) { <input type="search" name="searchKey"/> <input type="submit" value="按名称搜索"/> }*@ @* -----------3.Ajax-------------------------------------- 需要手工为Form添加些属性标签,用于锚点 模仿MVC框架的构建自己的“非介入式Javascript”模式 生成的form为: <form data-otf-ajax-updatetarget="#restaurantList" data-otf-ajax="true" action="/Reviews" method="post" novalidate="novalidate"> -------------------------------------------------------*@ <form method="post" action="@Url.Action("Index")" data-otf-ajax="true" data-otf-ajax-updatetarget="#restaurantList"> <input type="search" name="searchKey" data-oft-ajax-autocompleted="@Url.Action("AutoCompleted")" /> <input type="submit" value="按名称搜索" /> </form> <p> <strong> @if (User.Identity.IsAuthenticated) { Html.ActionLink("添加餐馆(登录后)", "Create"); } @Html.ActionLink("添加餐馆", "Create") </strong> </p> <div id="loding" hidden="hidden"> <img class="smallLoadingImg" src="@Url.Content("~/Content/images/loading.gif")" /> </div> @*Ajax异步更新目标html元素*@ <div id="restaurantList"> @{ if (@Model.Any() ) { //用Html.Partial出现不能显示的问题。原因待查,改用Html.RenderPartial Html.RenderPartial("_RestaurantPatialView",Model); } else { <img src="@Url.Content("~/Content/images/NotFoundCryingFace.jpg")"/> <strong><font color="red">抱歉,没有任何餐馆</font></strong> } } </div>
odf.js
$(function () { /*ajaxFrom*/ var ajaxFormSubmit = function() { var $form = $(this); var ajaxOption = { type: $form.attr("method"), url: $form.attr("action"), data: $form.serialize() }; $.ajax(ajaxOption).done(function(data) { var updateTargetId = $form.attr("data-otf-ajax-updatetarget"); var $updateTarget = $(updateTargetId); if ($updateTarget.length > 0) { var $returnHtml = $(data); $updateTarget.empty().append(data); $returnHtml.effect("highlight",6000); } }); return false; }; $("form[data-otf-ajax='true']").submit(ajaxFormSubmit); /*AutoCompleted*/ var submitAutoCompleted = function(event, ui) { var $input = $(this); $input.val(ui.item.label); var $form = $input.parents("form:first"); $form.submit(); }; var createAutoCompleted = function() { var $input = $(this); var ajaxOption = { source: $input.attr("data-oft-ajax-autocompleted"), //告诉AutoComplete组件去哪里获取数据 select: submitAutoCompleted, //选择某选项后,要处理的逻辑 }; $input.autocomplete(ajaxOption); } $("input[data-oft-ajax-autocompleted]").each(createAutoCompleted); /*分页,使用 PagedList.dll,*/ var getPage = function() { var $a = $(this); var ajaxOption = { url: $a.attr("href"), type: "post", data:$("form").serialize() //把表单中的数据也一起提交,比如,搜索关键字。结果是:在搜索关键字得到的结果上进行分页。 }; $.ajax(ajaxOption).done(function(data) { var updateTargetId = $a.parents("div.pagedList").attr("data-otf-ajax-updatetarget"); var $updateTarget = $(updateTargetId); var $returnHtml = $(data); $updateTarget.empty().append(data); $returnHtml.effect("highlight", 6000); }); return false; } $(".main-content").on("click", ".pagedList a",getPage); //$("#Pagination").pagination(); });
Review.js
$(function() { $(".isStar").prepend('<del class="hot"></del>'); //$(".isStar").append('<del class="hot"></del>'); });
-----------------------------------
TestAcitonResultController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Kwin.AspNetMvc.OdeToFood.Models; namespace Kwin.AspNetMvc.OdeToFood.Controllers { public class TestAcitonResultController : Controller { [ActionName("RedirectToAction")] //为Action起了别名RedirectToAction,就不能用原来的TestRedirectToAction访问了 public ActionResult TestRedirectToAction() { TestModel myTestModel = new TestModel() { IntParement = 100, StringPrarement = "TestModel->StringPrarement" }; return RedirectToAction("TextRouteParement", "TestAcitonResult", new { id = "12", myparementString = "keasy5", myparementInt = 30, testModel = myTestModel }); } public ActionResult TestRedirectToRoute() { return RedirectToRoute("Default", new { controller = "Home", action = "ForTestIndex", myparement = "keasy5" }); } public ActionResult TestFile() { return File(Server.MapPath("~/Content/site.css"), "text/css"); } public ActionResult TestJson() { return Json(new {Name = "Keasy5", Laction = "China"},JsonRequestBehavior.AllowGet); } public ActionResult TextRouteParement(string myparementString, int myparementInt, [ModelBinder(typeof(TestModelModelBinder))]TestModel testModel) { var controller = RouteData.Values["controller"]; var action = RouteData.Values["action"]; var id = RouteData.Values["id"]; var myparement2 = RouteData.Values["myparement"]; var myparementForRequestForm = Request["myparement"]??"N/A"; var messages = string.Format("controller={0},action={1},id={2},myparementString={3}, myparementInt={4},RouteData.Values[\"myparement\"]={5},testModel.IntParement={6},testModel.StringPrarement={7},Request[\"myparement\"]={8}", controller, action, id, myparementString, myparementInt, myparement2, testModel.IntParement, testModel.StringPrarement, myparementForRequestForm); return Content(messages); } } }
TestModelModelBinder.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using ModelBindingContext = System.Web.Http.ModelBinding.ModelBindingContext; namespace Kwin.AspNetMvc.OdeToFood.Models { public class TestModelModelBinder : DefaultModelBinder { #region IModelBinder Members public override object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext) { HttpRequestBase requestBase = controllerContext.HttpContext.Request; TestModel testModel = new TestModel(); testModel.IntParement = Convert.ToInt32(requestBase["IntParement"]); testModel.StringPrarement = requestBase["IntParement"]; /* var value = base.BindModel(controllerContext, bindingContext); if (bindingContext.ModelType == typeof(TestModel)) return value;*/ return testModel; } #endregion } }
--------------------------------------------------
TestActionFilerController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Kwin.AspNetMvc.OdeToFood.Filters; namespace Kwin.AspNetMvc.OdeToFood.Controllers { //[HandleError]可以注释掉因为在FilterConfig已经定义了全局的HandleError,为所有的controller使用了[HandleError] public class TestActionFilterController : Controller { public ActionResult Index() { string messages = "TestActionFilterController index"; return Content(messages); } [Authorize] public ActionResult TestAuthorize() { string messages = "TestAuthorize"; return Content(messages); } public ActionResult TestHandleError(int id = 0) { if (id == 0) { throw new Exception("我是故意的,测试HandleError用的异常!"); } /* 默认是显示一个bug异常页面, * 要是定制显示bug页面,先在web.cofng配置如下节点: * <system.web> <customErrors mode="On"/>*/ return Content("TestHandleError"); } public ActionResult TestLog() { return Content("TestHandleError"); } } }
---------------------------------------------------
FilterConfig.cs
using System.Web; using System.Web.Mvc; using Kwin.AspNetMvc.OdeToFood.Filters; namespace Kwin.AspNetMvc.OdeToFood { public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //在这里定义的是全局的Filter,意味着所有的Controller都启用这里注册的Filter filters.Add(new HandleErrorAttribute()); filters.Add(new LogAttribute()); } } }
Filters/LogAttribute.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http.Controllers; using System.Web.Mvc; namespace Kwin.AspNetMvc.OdeToFood.Filters { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class LogAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); //写系统日志 } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); } } }