ModelState.IsValid始终是正则表达式ValidationAttribute在MVC 4

问题描述:

在我的课假的,我有一个属性,像这样一个文件附件...ModelState.IsValid始终是正则表达式ValidationAttribute在MVC 4

public class Certificate { 
    [Required] 
    // TODO: Wow looks like there's a problem with using regex in MVC 4, this does not work! 
    [RegularExpression(@"^.*\.(xlsx|xls|XLSX|XLS)$", ErrorMessage = "Only Excel files (*.xls, *.xlsx) files are accepted")] 
    public string AttachmentTrace { get; set; } 
} 

我看不出有什么毛病我正则表达式,但我总是得到ModelState.IsValid为false。这看起来很琐碎和简单的正则表达式,我错过了什么?我是否需要编写自己的自定义验证?

我通过类型文件的常规输入填充AttachmentTrace:

<div class="editor-label"> 
    @Html.LabelFor(model => model.AttachmentTrace) 
</div> 
<div class="editor-field"> 
    @Html.TextBoxFor(model => model.AttachmentTrace, new { type = "file" }) 
    @Html.ValidationMessageFor(model => model.AttachmentTrace) 
</div> 

动作方法只是一个普通的动作:

public ActionResult Create(Certificate certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) 
    { 
     if (ModelState.IsValid) 
     { 
      // code ... 
     } 
     return View(certificate); 
    } 
+0

你是如何填充AttachmentTrace的? – 2013-03-12 19:34:54

+0

四十二,我通过类型文件的常规输入来填充它(只是在上面添加了一些代码)。谢谢。 – sammydc 2013-03-12 20:16:13

+0

那么,你在做什么,我会猜测AttachmentTrace的价值不是你期望的(文件名),而是像“System.Web.HttpPostedFileWrapper”而不是。 – 2013-03-12 20:47:20

好吧,这里我找到了解决办法。我确信有其他解决方案。模型生成过程中检测到

一个或多个验证错误:第一一点背景,因为我的应用程序使用EF代码优先迁移,指定在我的模型一个HttpPostedFileBase属性类型,加入迁移时产生这样的错误: System.Data.Entity.Edm.EdmEntityType :: EntityType 'HttpPostedFileBase'没有定义键。定义此 EntityType的密钥。 \ tSystem.Data.Entity.Edm.EdmEntitySet:EntityType: EntitySet'HttpPostedFileBases'基于没有定义键的类型'HttpPostedFileBase' 。

所以我真的不得不坚持使用AttachmentTrace属性的字符串类型。

的解决方案是采用ViewModel类是这样的:

public class CertificateViewModel { 
    // .. other properties 
    [Required] 
    [FileTypes("xls,xlsx")] 
    public HttpPostedFileBase AttachmentTrace { get; set; } 
} 

然后创建一个FileTypesAttribute像这样,我从this excellent post借用了这个代码。

public class FileTypesAttribute : ValidationAttribute { 
    private readonly List<string> _types; 

    public FileTypesAttribute(string types) { 
     _types = types.Split(',').ToList(); 
    } 

    public override bool IsValid(object value) { 
     if (value == null) return true; 
     var postedFile = value as HttpPostedFileBase; 
     var fileExt = System.IO.Path.GetExtension(postedFile.FileName).Substring(1); 
     return _types.Contains(fileExt, StringComparer.OrdinalIgnoreCase); 
    } 

    public override string FormatErrorMessage(string name) { 
     return string.Format("Invalid file type. Only {0} are supported.", String.Join(", ", _types)); 
    } 
} 

在控制器操作,我需要做出改变使用视图模型来代替,然后映射回使用AutoMapper(这是极好的方式)我的实体:

public ActionResult Create(CertificateViewModel certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) { 
     if (ModelState.IsValid) { 
      // Let's use AutoMapper to map the ViewModel back to our Certificate Entity 
      // We also need to create a converter for type HttpPostedFileBase -> string 
      Mapper.CreateMap<HttpPostedFileBase, string>().ConvertUsing(new HttpPostedFileBaseTypeConverter()); 
      Mapper.CreateMap<CreateCertificateViewModel, Certificate>(); 
      Certificate myCert = Mapper.Map<CreateCertificateViewModel, Certificate>(certificate); 
      // other code ... 
     } 
     return View(myCert); 
    } 

对于在AutoMapper,我创建了自己的类型转换器为HttpPostedFileBase如下:

public class HttpPostedFileBaseTypeConverter : ITypeConverter<HttpPostedFileBase, string> { 

    public string Convert(ResolutionContext context) { 
     var fileBase = context.SourceValue as HttpPostedFileBase; 
     if (fileBase != null) { 
      return fileBase.FileName; 
     } 
     return null; 
    } 
} 

就是这样。希望这可以帮助那些可能有同样问题的人。