要开始的基本问题:如何在模型中的对象列表上放置一个自定义的,不显眼的验证器?比如,说我的模型允许多个文件上传,因此我有一个文件列表,我希望我的验证器在每个文件上运行?
现在举一个具体的例子。我有一个自定义的,不引人注意的验证器,它检查文件扩展名是否在禁止扩展名列表中:
public class FileExtensionValidatorAttribute : ValidationAttribute, IClientValidatable {
protected static string[] PROHIBITED_EXTENSIONS = {
// ... List of extensions I don't allow.
};
public override bool IsValid(object value) {
if (value is IEnumerable<HttpPostedFileBase>) {
foreach (var file in (IEnumerable<HttpPostedFileBase>)value) {
var fileName = file.FileName;
if (PROHIBITED_EXTENSIONS.Any(x => fileName.EndsWith(x))) return false;
}
} else {
var file = (HttpPostedFileBase)value;
var fileName = file.FileName;
if (PROHIBITED_EXTENSIONS.Any(x => fileName.EndsWith(x))) return false;
}
return true;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
var modelClientVlidationRule = new ModelClientValidationRule {
ErrorMessage = this.ErrorMessageString,
ValidationType = "fileextension",
};
modelClientVlidationRule.ValidationParameters.Add("prohibitedextensions", string.Join("|", PROHIBITED_EXTENSIONS));
yield return modelClientVlidationRule;
}
}
在我的IsValid中注意我构建它以接受单个文件或文件列表。
在我的模型类中,我可以在单个HttpPostedFileBase上使用它:
[FileExtensionValidator(ErrorMessage = "Invalid Extension")]
public HttpPostedFileBase Upload { get; set; }
然后我在视图中附加到jquery的验证器:
jQuery.validator.addMethod("fileExtension", function (value, element, param) {
var extension = "";
var dotIndex = value.lastIndexOf('.');
if (dotIndex != -1) extension = value.substring(dotIndex + 1).toLowerCase();
return $.inArray(extension, param.prohibitedExtensions) === -1;
});
jQuery.validator.unobtrusive.adapters.add('fileextension', ['prohibitedextensions'], function (options) {
options.rules['fileExtension'] = {
prohibitedExtensions: options.params.prohibitedextensions.split('|')
};
options.messages['fileExtension'] = options.message;
});
这一切都很好,客户端和服务器端...但只在一个HttpPostedFileBase上。问题是我需要为用户提供上传一个或多个文件的能力。如果我将我的模型更改为:
[FileExtensionValidator(ErrorMessage = "Invalid Extension")]
public List<HttpPostedFileBase> Uploads { get; set; }
...客户端验证不再运行;只有服务器端工作。这在执行视图源时很明显。 &lt;输入&gt;生成的标记缺少它需要运行的所有data-val属性。在进行调试时,永远不会调用GetClientValidationRules。
我错过了什么?
这可能是因为我如何呈现它?我只是为HttpPostedFileBase使用EditorTemplate:
@model System.Web.HttpPostedFileBase
@Html.TextBoxFor(m => m, new { type = "file", size = 60 })
...我的观点呈现如下:
<p>@Html.EditorFor(m => m.Uploads)</p>
感谢任何建议。
答案 0 :(得分:3)
这就是我想出来的。
我实际上认为问题最终是由于MVC不知道我希望List上的Data Annotation应用于其所有成员。我也不应该这样想。
所以我只是在HttpPostedFileBase周围创建了一个“viewmodel”包装器,然后将我的验证器放在那里:
public class UploadedFile {
[FileExtensionValidator(ErrorMessage = "Invalid Extension")]
public HttpPostedFileBase File { get; set; }
}
然后,在我的实际模型中,我现在只使用这些列表:
public List<UploadedFile> Uploads { get; set; }
...当然没有更多的数据注释,因为它们现在处于UploadedFile。
然后,通过对视图和editortemplate的微小修改来使用它们,现在可以正常工作,客户端和服务器端。 (尽管如此,对我来说还是很笨拙。如果有人有更简单的方式,我仍然很乐意听到它。)