现在做网站用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是需要深入了解的,啃一下源码也是有收获的。欢迎大家补充。