使用自定义验证的 FileExtension Validation 会创建重复和无效的 data-* 属性
FileExtension Validation using custom validation creates duplicate and invalid data-* attributes
这个问题是在我尝试了 . I followed this article 中提到的答案之后提出的,但验证方式完全相同对于 image files
而不是文章中提到的 doc files
。
描述: 我有一个 input
控件 type=file
用于上传图像文件,它存在于 partialview
之一. partialview
在 button
的 click
上加载。并应用 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>
<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 class
[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));
}
}
您需要移动区块代码
$(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....
就像你在代码中的其他地方所做的那样。
至于 'fishy' data-val-*
属性,这正是您的代码生成的。您生成一个名为 fileextensions
的 ClientValidationRule
(ValidationType = "fileextensions";
代码)然后添加一个 属性 也命名为 fileextensions
(ValidationParameters.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() ....
编辑
您的代码不适用于 <input type="file" multiple="multiple" ... />
为此,您的 属性 需要 IEnumerable
(请注意对您的代码进行一些小改动)
[FileType("png, jpg, jpeg")]
public IEnumerable<HttpPostedFileBase> Files { get; set; }
然后验证属性需要检查集合中的每个文件
[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;
}
这个问题是在我尝试了 image files
而不是文章中提到的 doc files
。
描述: 我有一个 input
控件 type=file
用于上传图像文件,它存在于 partialview
之一. partialview
在 button
的 click
上加载。并应用 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>
<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 class
[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));
}
}
您需要移动区块代码
$(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....
就像你在代码中的其他地方所做的那样。
至于 'fishy' data-val-*
属性,这正是您的代码生成的。您生成一个名为 fileextensions
的 ClientValidationRule
(ValidationType = "fileextensions";
代码)然后添加一个 属性 也命名为 fileextensions
(ValidationParameters.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() ....
编辑
您的代码不适用于 <input type="file" multiple="multiple" ... />
为此,您的 属性 需要 IEnumerable
(请注意对您的代码进行一些小改动)
[FileType("png, jpg, jpeg")]
public IEnumerable<HttpPostedFileBase> Files { get; set; }
然后验证属性需要检查集合中的每个文件
[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;
}