【问题标题】:JQuery Validation: Add custom method to validate on submitJQuery Validation:添加自定义方法以在提交时进行验证
【发布时间】:2012-06-11 18:53:20
【问题描述】:

我有一个简单的 js/jquery 函数,我想在提交表单时运行以进行额外验证。我的表单允许输入多个文件,我想确保所有文件的总和低于我设置的文件限制。

var totalFileSize = 0;
$("input:file").each(function () {
    var file = $(this)[0].files[0];
    if (file) totalFileSize += file.size;
});

return totalFileSize < maxFileSize; // I've set maxFileSize elsewhere.

问题是我想将它作为 jquery 验证的一部分运行。在其他地方,我的表单使用标准的 MVC3 验证和我编写的单独的自定义非侵入式验证。如果这个文件大小验证器失败,我希望它的行为与其他 jquery 验证器一样:显然,我想停止提交,并在与其他验证器相同的摘要框中显示错误消息。

有没有什么方法可以调用像这样的简单方法作为提交验证的一部分?我考虑过 $.validator.addMethod,但如果我将它添加到每个 input:file 元素中,它将在提交时多次运行相同的验证器,从而多次显示错误消息。如果有办法添加此验证器但不将其绑定到任何元素,那就太好了。

【问题讨论】:

    标签: jquery asp.net-mvc-3 jquery-validate unobtrusive-validation


    【解决方案1】:

    您可以编写自定义验证属性并注册自定义客户端适配器。让我详细说明。

    假设您有一个视图模型来表示要上传的文件列表,并且您希望将所有上传文件的总大小限制为 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。

    【讨论】:

    • 这太不可思议了!非常感谢您的详尽示例;我根本不知道视图可以多次绘制相同的 TextBoxFor 列表,但只呈现一次客户端规则,并且 jquery 验证会将它们全部称为一个。优秀!给其他读者的一点提示:IE 还不支持 javascript 文件大小查找(从 IE9 开始),因此 my/Darin 的 JS 代码实际上会出现异常,导致无法验证任何内容。验证方法中的一个简单if (element.files) 解决了这个问题。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    相关资源
    最近更新 更多