ASP.NET MVC模型部分驗證


在很多情況下,我們為了代碼的復用可能會存在ViewModel共用的情形。比方說,web應用中常常會遇到的一個需求就是用戶找回密碼的功能。用戶首先要驗證通過驗證郵箱(通常是用戶名)來獲取驗證碼,然后再進行密碼重置。那么此時假設需要4個字段:Email、Password、ConfirmPassword、VerifiCode。那么就需要2個單獨的頁面中隊這一個model進行分步驗證了。google了一下,發現了一些有價值的解決方案。為了說明問題,寫了一個小的Demo如下:

ViewModel:

public class ForgotPasswordMdl
{
    [Display(Name = "郵箱")]
    [Required(ErrorMessage = "請輸入注冊時的郵箱地址")]
    public string Email { get; set; }
    [Display(Name = "密碼")]
    [Required(ErrorMessage = "密碼不能為空")]
    public string Password { get; set; }
    [Display(Name = "確認密碼")]
    [Required(ErrorMessage = "確認密碼不能為空")]
    public string ConfirmPassword { get; set; }
}
View Code

2個簡單的頁面一個驗證郵箱、一個驗證重置的密碼,就不列出。

先來看方案1:

[HttpPost]
public ActionResult EmailValidate([Bind(Exclude = "Password")]ForgotPasswordMdl model)
{
    var array = new string[] { "Password", "ConfirmPassword" };
    foreach (var key in array)
    {
        ModelState.Remove(key);
    }
    if (!ModelState.IsValid) return View(model);
    return RedirectToAction("ResetPassword");
}
View Code

當然如果你不想每次都定義一些需要篩選掉的字段的集合。你可以這么做:

[HttpPost]
public ActionResult EmailValidate([Bind(Exclude = "Password")]ForgotPasswordMdl model)
{
    var keysWithNoIncomingValue = ModelState.Keys.Where(x => !ValueProvider.ContainsPrefix(x));
    foreach (var key in keysWithNoIncomingValue)
        ModelState[key].Errors.Clear();

    if (!ModelState.IsValid) return View(model);
    return RedirectToAction("ResetPassword");
}
View Code

思路很簡單就是從模型中移除不需要驗證的元素。
這個方法雖然有效,但是它沒有分離應用程序的關注點。當需要忽略的字段一旦很多的時候可能就會變得很繁瑣(當然如果需要驗證的字段很少,你也可以這么做:ModelState.IsValidField(""))。

再來看方案2:

//參看鏈接:http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/
public class ValidateOnlyIncomingValuesAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var modelState = filterContext.Controller.ViewData.ModelState;
        var valueProvider = filterContext.Controller.ValueProvider;
        valueProvider.ContainsPrefix("Password");
        var keysWithNoIncomingValue = modelState.Keys.Where(x => !valueProvider.ContainsPrefix(x));
        foreach (var key in keysWithNoIncomingValue)
            modelState[key].Errors.Clear();
    }
}
View Code

控制器代碼:

思路也很簡單:就是使用過濾器,在Action執行過程中移除未提供的字段。

最后來看方案3:

//參看鏈接:http://www.codeproject.com/Articles/293894/Partial-Validation-with-Data-Annotations-in-ASP-NE
public class IgnoreModelErrorsAttribute : ActionFilterAttribute
{
    private string keysString;

    public IgnoreModelErrorsAttribute(string keys)
        : base()
    {
        this.keysString = keys;
    }
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState;
        string[] keyPatterns = keysString.Split(new char[] { ',' },
                 StringSplitOptions.RemoveEmptyEntries);
        for (int i = 0; i < keyPatterns.Length; i++)
        {
            string keyPattern = keyPatterns[i]
                .Trim()
                .Replace(@".", @"\.")
                .Replace(@"[", @"\[")
                .Replace(@"]", @"\]")
                .Replace(@"\[\]", @"\[[0-9]+\]")
                .Replace(@"*", @"[A-Za-z0-9]+");
            IEnumerable<string> matchingKeys =
               modelState.Keys.Where(x => System.Text.RegularExpressions.Regex.IsMatch(x, keyPattern));
            foreach (string matchingKey in matchingKeys)
                modelState[matchingKey].Errors.Clear();
        }
    }
}
View Code

做法和上面的方案2一樣,就是通過過濾器移除掉不需要驗證的字段。它們其實是一樣的。只不過方案2是通過ValueProvider值提供程序來檢索對應的字段是否有值來實現篩選的。
當然期待希望能有更多滴解決辦法!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM