4

開始的基本問題:如何在模型中的對象列表上放置自定義,不顯眼的驗證器?就像說,我的模型允許多個文件上傳,因此我有一個文件列表,我希望我的驗證程序在每個文件上運行?MVC3對象列表上的自定義驗證器

現在是一個具體的例子。我有一個習慣,不引人注目的驗證檢查,看看如果文件擴展名是不禁止的擴展名列表中:

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; } 

...客戶端驗證不再運行;只有服務器端工作。這在做視圖源時很明顯。生成的<輸入>標記缺少它需要運行的所有data-val屬性。在進行調試時,GetClientValidationRules永遠不會被調用。

我錯過了什麼?

難道這是因爲我如何渲染它?我只是用EditorTemplate爲HttpPostedFileBase:

@model System.Web.HttpPostedFileBase 
@Html.TextBoxFor(m => m, new { type = "file", size = 60 }) 

...和我的觀點呈現這樣的:

<p>@Html.EditorFor(m => m.Uploads)</p> 

任何建議表示讚賞。

+0

您的視圖是否呈現多個文本框?上載後,客戶端在哪裏/如何存儲值? – Terry

+0

是的。多個文本框渲染正常,它們正在上傳到服務器。 (服務器端驗證在它們兩個上運行正確。)但是,對不起,我不明白你的第二個問題。 –

+0

你可以上傳幾個文件並使用DOM檢查器來顯示呈現的HTML嗎? – Terry

回答

3

這是我想出來的。

我實際上認爲問題最終是由於MVC不知道我希望List上的Data Annotation應用於其所有成員。我想也不應該。

所以我只是使周圍HttpPostedFileBase一個「視圖模型」包裝,並把我的驗證有:

public class UploadedFile { 
    [FileExtensionValidator(ErrorMessage = "Invalid Extension")] 
    public HttpPostedFileBase File { get; set; } 
} 

然後,在我的實際模型,我剛纔用的那些,而不是一個列表:

public List<UploadedFile> Uploads { get; set; } 

...當然這裏沒有更多的數據註解,因爲它們現在處於UploadedFile中。

然後,通過對view和editortemplate進行微小的修改來使用這些,現在可以工作在a-ok,客戶端和服務器端。 (儘管如此,如果任何人有更簡單的方法,我仍然樂於聽到它。)