您可以編寫自定義驗證屬性並註冊自定義客戶端適配器。讓我詳細說明一下。
假設您有一個視圖模型來表示要上傳的文件列表,並且您希望將所有上傳文件的總大小限制爲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; }
}
現在讓我們定義這顯然會執行服務器端驗證,但除此之外,它還可將實施IClientValidatable接口,允許註冊一個該自定義[MaxFileSize]
驗證屬性自定義的不顯眼的客戶端驗證規則,將允許在客戶端上轉置此驗證邏輯(顯然,僅適用於支持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。
這太不可思議了!非常感謝你的徹底例證。我根本不知道該視圖可能會多次繪製相同的TextBoxFor列表,但只會渲染一次客戶端規則,並且jQuery驗證會將它們全部視爲一個整體。優秀!其他讀者只需要注意一點:IE不支持JavaScript文件大小查找(截至IE9),因此我的/ Darin的JS代碼實際上會異常排除,導致無法驗證。驗證方法中的一個簡單的if(element.files)修復了這個問題。再次感謝! –