使用自定义验证的 FileExtension Validation 创建重复和无效的 data-* 属性

Posted

技术标签:

【中文标题】使用自定义验证的 FileExtension Validation 创建重复和无效的 data-* 属性【英文标题】:FileExtension Validation using custom validation creates duplicate and invalid data-* attributes 【发布时间】:2016-01-30 14:47:57 【问题描述】:

这个问题是在我尝试了 my previous question 中提到的答案之后提出的。我遵循 this article 完全相同的方式,但验证 image files 而不是文章中提到的 doc files

说明:我有一个input 控件type=file,用于上传图像文件,这存在于partialview 之一中。 partialview 被加载到 clickbutton 上。要应用model 中提到的validations,请将unobtrusive 显式添加到form。但是按照上述文章中提到的所有设置后,我无法验证submit 上的文件,而且unobtrusive validation 创建的data-* 非常可疑,或者最好说无效。下面是显示我的设置的代码,这里是html,它是由具有无效data-* 属性的不显眼验证创建的,可能是因为验证失败。

<input data-charset="file" data-val="true" data-val-fileextensions="" data-val-fileextensions-fileextensions="png,jpg,jpeg" id="File" multiple="multiple" name="File" type="file" value="">

加载部分视图 Js

$('.getpartial').on('click', function () 
    $('.loadPartial').empty().load('/Home/GetView',function () 
        var form = $('form#frmUploadImages');
        form.data('validator', null);
        $.validator.unobtrusive.parse(form);
        $(function () 
            jQuery.validator.unobtrusive.adapters.add('fileextensions', ['fileextensions'], function (options) 
                var params = 
                    fileextensions: options.params.fileextensions.split(',')
                ;
                options.rules['fileextensions'] = params;
                if (options.message) 
                    options.messages['fileextensions'] = options.message;
                
            );

            jQuery.validator.addMethod("fileextensions", function (value, element, param) 
                var extension = getFileExtension(value);
                var validExtension = $.inArray(extension, param.fileextensions) !== -1;
                return validExtension;
            );

            function getFileExtension(fileName) 
                var extension = (/[.]/.exec(fileName)) ? /[^.]+$/.exec(fileName) : undefined;
                if (extension != undefined) 
                    return extension[0];
                
                return extension;
            ;
        (jQuery));
    )
)

模型类

public class ImageUploadModel

    [FileValidation("png|jpg|jpeg")]
    public HttpPostedFileBase File  get; set; 

查看

@model ProjectName.Models.ImageUploadModel

@using (Html.BeginForm("UploadImages", "Admin", FormMethod.Post, htmlAttributes: new  id = "frmUploadImages", novalidate = "novalidate", autocomplete = "off", enctype = "multipart/form-data" ))

    <div class="form-group">
        <span class="btn btn-default btn-file">
            Browse @Html.TextBoxFor(m => m.File, new  type = "file", multiple = "multiple", data_charset = "file" )
        </span>&nbsp;
        <span class="text-muted" id="filePlaceHolder">No files selected</span>
        @Html.ValidationMessageFor(m => m.File, null, htmlAttributes: new  @class = "invalid" )
    </div>
    <div class="form-group">
        <button class="btn btn-primary addImage pull-right">
            <i class="fa fa-upload"></i> Upload
        </button>
    </div>

最后是我的 CustomFileValidation

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FileValidationAttribute : ValidationAttribute, IClientValidatable

    private List<string> ValidExtensions  get; set; 

    public FileValidationAttribute(string fileExtensions)
    
        ValidExtensions = fileExtensions.Split('|').ToList();
    

    public override bool IsValid(object value)
    
        HttpPostedFileBase file = value as HttpPostedFileBase;
        if (file != null)
        
            var fileName = file.FileName;
            var isValidExtension = ValidExtensions.Any(y => fileName.EndsWith(y));
            return isValidExtension;
        
        return true;
    

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    
        var rule = new ModelClientFileExtensionValidationRule(ErrorMessage, ValidExtensions);
        yield return rule;
    

public class ModelClientFileExtensionValidationRule : ModelClientValidationRule

    public ModelClientFileExtensionValidationRule(string errorMessage, List<string> fileExtensions)
    
        ErrorMessage = errorMessage;
        ValidationType = "fileextensions";
        ValidationParameters.Add("fileextensions", string.Join(",", fileExtensions));
    

【问题讨论】:

您的 2 个 jQuery.validatorgetFileExtension() 函数不应在 $('.getpartial').on('click', function () 内 - 将它们移到方法之前(包括 $(function () .... (jQuery)); 在内的所有内容) 此外,该代码只会验证一个文件,因此它可能不适用于multiple="multiple"(尽管可以对其进行修改) @StephenMuecke.. 太棒了.. 成功了.. 但是,data-* 属性,即data-val-fileextensions="" data-val-fileextensions-fileextensions="png,jpg,jpeg"DOM 中仍然保持不变。这不是invalid 吗? data-val-fileextensions-fileextensions="png,jpg,jpeg" 的生成是因为 ModelClientFileExtensionValidationRule() 方法具有 ValidationType = "fileextensions"; 和 `ValidationParameters.Add("fileextensions", ..)` - 这有点令人困惑,因为它们应该是不同的名称.而data-val-fileextensions="" 是因为你没有错误信息。 稍后我会添加一个答案,解释原因(以及multiple="multiple" 的潜在问题) 【参考方案1】:

你需要移动块代码

$(function () 
  ....
(jQuery));

$('.getpartial').on(..) 函数内部到它之前,这样它就是

<script>
  $(function () 
    ....
  (jQuery));

  $('.getpartial').on('click', function ()  // or just $('.getpartial').click(function() 
    $('.loadPartial').empty().load('/Home/GetView',function ()  // recommend .load('@Url.Action("GetView", "Home")', function() 
      var form = $('form#frmUploadImages');
      form.data('validator', null);
      $.validator.unobtrusive.parse(form);
    );
  );
</script>

当前您加载内容,重新解析验证器,然后将方法添加到 jquery 验证中,但为时已晚(验证器已被解析)

旁注:您不需要将验证函数包装在$(function () 中。它可以被删除,只需使用$.validator... 而不是jQuery.validator....,就像您在代码的其他地方所做的那样。

至于“可疑”data-val-* 属性,这正是您的代码生成的。您生成一个名为fileextensionsClientValidationRuleValidationType = "fileextensions"; 代码),然后添加它的一个属性,也称为fileextensionsValidationParameters.Add("fileextensions", ..) 代码生成data-val-fileextensions-fileextensions="png,jpg,jpeg"。至于data-val-fileextensions="",即生成来存储错误消息,但您还没有生成一个,所以它是一个空字符串。

我建议对您的代码进行一些更改。

    将其重命名为FileTypeAttribute,以便您可以灵活地 添加其他文件验证属性,例如 FileSizeAttribute 验证最大大小。 在构造函数中,生成默认错误信息,例如 添加private const string _DefaultErrorMessage = "Only the following file types are allowed: 0"; 并在构造函数的最后一行包含ErrorMessage = string.Format(_DefaultErrorMessage, string.Join(" or ", ValidExtensions));ValidationParameters.Add("fileextensions", ...) 更改为(比如说) ValidationParameters.Add("validtypes", ...) 所以它生成 data-val-fileextensions-validtypes="png,jpg,jpeg" 有点 更有意义(请注意,您需要将脚本更改为 ...add('fileextensions', ['validtypes'], function() ....

编辑

您的代码不适用于 &lt;input type="file" multiple="multiple" ... /&gt; 为此,您的属性需要为 IEnumerable(请注意对您的代码进行一些小的更改)

[FileType("png, jpg, jpeg")]
public IEnumerable<HttpPostedFileBase> Files  get; set; 

那么validation属性需要检查集合中的每个文件

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FileTypeAttribute : ValidationAttribute, IClientValidatable

    private const string _DefaultErrorMessage = "Only the following file types are allowed: 0";
    private IEnumerable<string> _ValidTypes  get; set; 

    public FileTypeAttribute(string validTypes)
    
        _ValidTypes = validTypes.Split(',').Select(s => s.Trim().ToLower());
        ErrorMessage = string.Format(_DefaultErrorMessage, string.Join(" or ", _ValidTypes));
    

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    
        IEnumerable<HttpPostedFileBase> files = value as IEnumerable<HttpPostedFileBase>;
        if (files != null)
        
            foreach(HttpPostedFileBase file in files)
            
                if (file != null && !_ValidTypes.Any(e => file.FileName.EndsWith(e)))
                
                    return new ValidationResult(ErrorMessageString);
                
            
        
        return ValidationResult.Success;
    
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    
        var rule = new ModelClientValidationRule
        
            ValidationType = "filetype",
            ErrorMessage = ErrorMessageString
        ;
        rule.ValidationParameters.Add("validtypes", string.Join(",", _ValidTypes));
        yield return rule;
    

最后脚本需要检查每个文件

$.validator.unobtrusive.adapters.add('filetype', ['validtypes'], function (options) 
    options.rules['filetype'] =  validtypes: options.params.validtypes.split(',') ;
    options.messages['filetype'] = options.message;
);

$.validator.addMethod("filetype", function (value, element, param) 
    for (var i = 0; i < element.files.length; i++) 
        var extension = getFileExtension(element.files[0].name);
        if ($.inArray(extension, param.validtypes) === -1) 
            return false;
        
    
    return true;
);

function getFileExtension(fileName) 
    if (/[.]/.exec(fileName)) 
        return /[^.]+$/.exec(fileName)[0].toLowerCase();
    
    return null;

【讨论】:

好的哥们.. 我明白.. 会做所有的改变和感谢FileSizeValidation 链接.. 你还说你会提到多文件上传处理.. 你能不能显示相同如果可能的话? 第一个问题是您的属性是HttpPostedFileBase 而不是IEnumerable&lt;HttpPostedFileBase&gt;。然后是IsValid() 方法也基于一个文件(不是多个)并且脚本也在验证一个文件(不是循环遍历每个文件)的问题。没时间,但我明天会玩弄它,让你知道。 好的兄弟..非常感谢..我也会尽力解决..:) 查看更新(结合我之前提出的一些建议) public IEnumerable&lt;HttpPostedFileBase&gt; Files get; set; 必须是您的ImageUploadModel 模型的属性(而不是您当前的public HttpPostedFileBase File get; set; ),然后在视图中使用@Html.TextBoxFor(m =&gt; m.Files, new type = "file", multiple = "multiple" )

以上是关于使用自定义验证的 FileExtension Validation 创建重复和无效的 data-* 属性的主要内容,如果未能解决你的问题,请参考以下文章

Enityt模型特性

自定义不显眼的验证方法未按照文档触发

AngularJS自定义验证指令 - 如何避免使用隔离范围

向 Spree 前端添加自定义客户端表单验证

验证密码和确认密码

为什么未在formarray自定义验证值更改中设置错误