您可以编写自定义验证属性并注册自定义客户端适配器。让我详细说明。
假设您有一个视图模型来表示要上传的文件列表,并且您希望将所有上传文件的总大小限制为 2 MB。您的视图模型肯定看起来类似于:
public class MyViewModel
{
[MaxFileSize(2 * 1024 * 1024, ErrorMessage = "The total file size should not exceed {0} bytes")]
public IEnumerable<HttpPostedFileBase> Files { get; set; }
}
现在让我们定义这个自定义[MaxFileSize]
验证属性,它显然将执行服务器端验证,但除此之外,它还将实现IClientValidatable接口,允许注册一个自定义的不显眼的客户端验证规则,该规则将允许在客户端上转置此验证逻辑(显然只有对于支持 HTML5 File API的浏览器,您可以在客户端上确定所选文件的大小 => IE 对于这样的事情和使用此浏览器的用户来说是完全不可能的将不得不通过仅服务器端验证来满足他们或做一些更好的事情 - 使用 Internet Explorer 来完成这个世界上唯一有用的任务,这种软件和平可以做:一旦你得到一个干净的 Windows 安装,就通过互联网下载一个真正的网页浏览器):
public class MaxFileSizeAttribute : ValidationAttribute, IClientValidatable
{
public MaxFileSizeAttribute(int maxTotalSize)
{
MaxTotalSize = maxTotalSize;
}
public int MaxTotalSize { get; private set; }
public override bool IsValid(object value)
{
var files = value as IEnumerable<HttpPostedFileBase>;
if (files != null)
{
var totalSize = files.Where(x => x != null).Sum(x => x.ContentLength);
return totalSize < MaxTotalSize;
}
return true;
}
public override string FormatErrorMessage(string name)
{
return base.FormatErrorMessage(MaxTotalSize.ToString());
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(MaxTotalSize.ToString()),
ValidationType = "maxsize"
};
rule.ValidationParameters["maxsize"] = MaxTotalSize;
yield return rule;
}
}
下一步是有一个控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
// Server side validation failed => redisplay the view so
// that the user can fix his errors
return View(model);
}
// Server side validation passed => here we can process the
// model.Files collection and do something useful with the
// uploaded files knowing that their total size will be smaller
// than what we have defined in the custom MaxFileSize attribute
// used on the view model
// ...
return Content("Thanks for uploading all those files");
}
}
和相应的观点:
@model MyViewModel
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
(function ($) {
$.validator.unobtrusive.adapters.add('maxsize', ['maxsize'], function (options) {
options.rules['maxsize'] = options.params;
if (options.message) {
options.messages['maxsize'] = options.message;
}
});
$.validator.addMethod('maxsize', function (value, element, params) {
var maxSize = params.maxsize;
var $element = $(element);
var files = $element.closest('form').find(':file[name=' + $element.attr('name') + ']');
var totalFileSize = 0;
files.each(function () {
var file = $(this)[0].files[0];
if (file && file.size) {
totalFileSize += file.size;
}
});
return totalFileSize < maxSize;
}, '');
})(jQuery);
</script>
@Html.ValidationMessageFor(x => x.Files)
@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
@foreach (var item in Enumerable.Range(1, 3))
{
@Html.TextBoxFor(x => x.Files, new { type = "file" })
}
</div>
<button type="submit">OK</button>
}
显然,这里显示的 javascript 在视图中没有任何作用。它必须进入视图可以引用的单独的可重用 javascript 文件。我在此处将其包含在内,以提高可读性和重现场景的便利性,但在现实世界中,永远不要编写内联 javascript。