現在做網站用mvc越來越普及了,其好處就不說了,在這里只記錄一些很多人都容易忽視的地方。
引用本地css和js文件的寫法
這應該是最不受重視的地方,有同事也說我有點小題大作,但我覺得用mvc還是得有一個好習慣,對於維護那肯定是有幫助的。
首先是下面的代碼(推薦寫法)
<link href="@Url.Content("~/content/style.css")" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.9.1.min.js")"></script>
很少人會這樣寫,大多數人應該是這樣
<link href="/content/style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="/scripts/jquery-1.9.1.min.js"></script>
有什么區別,推薦的寫法會要多一點代碼,我也發現很多人都喜歡能省就省,而且也會說渲染出來的html代碼和第二種沒啥區別。是的,但是如果部署成站點下的虛擬目錄情況就不一樣了,第二種寫法可能就會給你帶來災難了,所以還是開始就勤快點吧,以后就不會痛苦了。
超鏈接的寫法
推薦寫法
<a href="@Url.Action("index","home")">首頁</a>
很多人會這樣寫
<a href="/home/index">首頁</a>
兩者的區別還是在於維護,一旦改變了路由規則,那么第二種方式改起來還是頭疼的,第一種方式就不需要作任何改動了。
模型驗證
盡管mvc自帶的驗證方式已經有很多了,但是在開發過程中總會有一些特殊的地方。舉例:用戶的手機號為非必填項,如果用戶填寫了手機號則需驗證其合法性。
[Required] [RegularExpression(@"^1[3|4|5|8]\d{9}$")] public string Cellphone { get; set; }
上面的代碼不能滿足非必填項這個要求,最直接的辦法就是這樣寫(先去掉上面的驗證方式)
public ActionResult Create(UserModel model) { if (ModelState.IsValid) { if (!string.IsNullOrEmpty(model.Cellphone) && !Regex.IsMatch(model.Cellphone, @"^1[3|4|5|8]\d{9}$")) { return View(model); } } }
這種方式總感覺很笨笨的,而且也不美觀,職責也有點亂,驗證規則也最好不應該出現在Controller中。怎么解決?很簡單,往往很多人都忽略了CustomValidation
[CustomValidation(typeof(ValidationUtil), "ValidateCellphone")] public string Cellphone { get; set; }
再定義一個驗證類
public static class ValidationUtil { public static ValidationResult ValidateCellphone(string cellphone) { if (!string.IsNullOrEmpty(cellphone) && !Regex.IsMatch(cellphone, @"^1[3|4|5|8]\d{9}$")) { return new ValidationResult("錯誤的手機號碼。示例:13800000000"); } return ValidationResult.Success; } }
這樣就可以在Controller中去掉那塊難看的代碼了,驗證也可以集中維護了,代碼也顯得優雅一些了。當然還有其他的方式,就是在Model中實現IValidatableObject也可以達到效果。
模型綁定
當一個表單可能是維護多個模型時,我發現之前有同事是這樣做的
public ActionResult Create() { var domain = new DomainModel() { Name = Request.Form["domainName"], ... }; var channel = new ChannelModel() { Name = Request.Form["channelName"], ... }; }
看上去有點丑陋還要寫好多代碼哦,而且模型變化,改動的代碼還是蠻多的,其實mvc是支持綁定多個模型的,只需要這樣寫
[HttpPost] public ActionResult Create([Bind(Prefix = "domain")]DomainModel domain, [Bind(Prefix = "channel")]ChannelModel channel) { }
前端代碼只需要簡單變動一下
域名:@Html.TextBox("domain.name", string.Empty)
頻道名稱:@Html.TextBox("channel.name", string.Empty)
這樣就可以將元數據綁定到不同的模型上去了
其他細節點
對於Action的參數也盡量這樣使用
[HttpPost] public ActionResult Bandwidth(DateTime start, DateTime end, string serviceId) { }
而不是這樣
[HttpPost] public ActionResult Bandwidth() { DateTime start = DateTime.Parse(Request.Form["start"]); DateTime end = DateTime.Parse(Request.Form["end"]); string serviceId = Request.Form["serviceId"]; } [HttpPost] public ActionResult Bandwidth(FormCollection form) { DateTime start = DateTime.Parse(form.Get("start")); DateTime end = DateTime.Parse(form.Get("end")); string serviceId = form.Get("serviceId"); }
mvc自帶的json序列化功能比較弱,所以會引入第三方類庫,這時會出現這樣的用法
return Content(JsonConvert.SerializeObject(data));
當然不建議這樣使用,還是那個要素,不利於維護,推薦的做法就是覆寫Json方法
protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) { return new NewJsonResult { Data = data, ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior }; } class NewJsonResult : JsonResult { public override void ExecuteResult(ControllerContext context) { context.MustNotNull("context"); if (JsonRequestBehavior == JsonRequestBehavior.DenyGet && String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("若要允許 GET 請求,請將 JsonRequestBehavior 設置為 AllowGet。"); } HttpResponseBase response = context.HttpContext.Response; if (!String.IsNullOrEmpty(ContentType)) { response.ContentType = ContentType; } else { response.ContentType = "application/json"; } if (ContentEncoding != null) { response.ContentEncoding = ContentEncoding; } else { response.ContentEncoding = System.Text.Encoding.UTF8; } if (Data != null) { response.Write(JsonConvert.SerializeObject(Data, new DataTableConverter(), new JavaScriptDateTimeConverter())); } } }
再配合過濾器,如將異常捕獲統一寫在OnException(ExceptionContext filterContext)中,操作日志寫在OnActionExecuting(ActionExecutingContext filterContext)和OnActionExecuted(ActionExecutedContext filterContext)中,認證寫在OnAuthorization(AuthorizationContext filterContext)中等等,經過一番考慮之后相信一個mvc項目變得可維護性就更高了,代碼也整潔清爽,職責也比較明確。
暫時就想到這么多,后續再慢慢補充。總之要用好mvc是需要深入了解的,啃一下源碼也是有收獲的。歡迎大家補充。