[鐵人賽Day16] ASP.Net Core MVC 進化之路 - Model Validation(2) / 自訂及遠端驗證

本文將延續上一篇還沒講完的Model Validation,

繼續介紹ASP.Net Core MVC中自訂及遠端驗證的使用方式。

自訂驗證

雖然預設已有許多實用的[ValidationAttribute] ,

但難免還是會遇到需要自訂驗證的時候。

這個部分跟ASP.Net MVC5沒有太大的改變,

透過繼承ValidationAttribute 可以幫我們實作自訂的後端驗證

即可迅速的打造一個屬於自己的[ValidationAttribute] 。

 

因為預設並沒有提供判斷日期先後的Attribute,

所以筆者下方來實作一個[DateAfter] 。

DateAfterAttribute.cs

public class DateAfterAttribute : ValidationAttribute
{
    private DateTime start;

    public DateAfterAttribute(string dateString, string format = "yyyy/MM/dd")
    {
        start = DateTime.ParseExact(dateString, format, null);
    }


    public override bool IsValid(object value)
    {
        var date = (DateTime)value;

        if (date.Ticks > start.Ticks)
        {
            return true;
        }
        return false;
    }
}

 

其實步驟相當簡單

首先繼承ValidationAttribute 抽象類別,

複寫Valid() 方法後就完成一半了。

要注意建構子中可定義使用時所需傳入的參數

[DateAfter("2020/1/1")] ,

建構子中第二個參數foramt 使用預設值的方式,

可使日期格式較具彈性變化。

Valid() 中的參數value為前端表單欄位中要驗證的值,

因為型別為object所以記得要轉型

 

使用時可以省略Attribute字樣(DataAfterAttribute),

最後來把它掛到要驗證的屬性上。

public class Book
{
    [BindRequired]
    public int Id { get; set; }

    [Required]
    public string Title{ get; set; }

    [DateAfter("2020/1/1", ErrorMessage = "your {0} should after 2020/1/1")]
    public DateTime PublishDate { get; set; }
}

 

測試結果如下。

 

要加入自訂的前端驗證步驟會稍稍麻煩一點,

首先要實作 IClientValidator介面,

我們來修改上面的DateAfterAttribute

public class DateAfterAttribute : ValidationAttribute, IClientModelValidator
{
    private DateTime start;

    public DateAfterAttribute(string dateString, string format = "yyyy/MM/dd")
    {
        start = DateTime.ParseExact(dateString, format, null);
    }
        
    public override bool IsValid(object value)
    {
        var date = (DateTime)value;

        if (date.Ticks > start.Ticks)
        {
            return true;
        }
        return false;
    }

    public void AddValidation(ClientModelValidationContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }
        //方式一
        MergeAttribute(context.Attributes, "data-val", "true");
        MergeAttribute(context.Attributes, "data-val-publishdate", "your publishdate should after 2020/12/30(前端驗證)");

        //方式二
        //context.Attributes["data-val"] = "true";
        //context.Attributes["data-val-publishdate"] = "your publishdate should after 2020/12/30(前端驗證)";
    }

    private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
    {
        if (attributes.ContainsKey(key))
        {
            return false;
        }
        attributes.Add(key, value);
        return true;
    }
}

AddValidation方法只是在定義前端欄位的觸發條件及錯誤訊息,

盡量按照data-val-{property_name}的格式,

其中{property_name}要與前端設定的script相符。

除了透過MergeAttribute的方法,

也可以直接針對Context.Attributes操作(IDictionary)。

 

最後要在前端.cshtml加上一小段script。

@section scripts{
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
    <script>
        $.validator.addMethod("publishdate",
            function (value, element, param) {
                var date = new Date(value);
                return date >new Date('2020/12/30');
            });

        $.validator.unobtrusive.adapters.addBool("publishdate");
    </script>
}

 

測試結果

 

遠端驗證

遠端驗證(Remote)是什麼呢?

它是一種很像前端的後端驗證。

在執行過程不仔細觀察你會以為它是前端驗證(因為不會閃一下),

其原理是藉ajax方式與後端溝通。

常用於「檢查欄位名稱是否與資料庫重複」。

透過掛上[Remote] 就可以簡單完成,

驗證過程會呼叫遠端的Action,

最後透過回傳的Json格式比對驗證結果。

 

接著繼續針對Book修改,

假設書名(Title)是不能夠重複的,

我們這部分透過遠端驗證來實現。

Book.cs

public class Book 
{
    [BindRequired]
    public int Id { get; set; }

    [Remote(action: "CheckRepeatTitle", controller: "Sample")]
    [Required]
    public string Title{ get; set; }
        
    [DateAfter("2020/12/30", ErrorMessage = "your {0} should after 2020/12/30")]
    public DateTime PublishDate { get; set; }

    public DateTime StartDate { get; set; }

    public DateTime EndDate { get; set; }
}

 

最後在SampleControllerCheckRepeatTitle實作方法。

public IActionResult CheckRepeatTitle(string Title)
{
    //Check title repeat state from database
    var isBookTitleRepeat = true;
    if (isBookTitleRepeat)
    {
        return Json($"{Title} is already in use.");
    }

    return Json(true);
}

 

大功告成後來測試一下結果。

遠端驗證使用起來其實非常爽,

如有需遠端驗證更進階應用的朋友可參考MSDN

 

驗證的部分就先介紹到這邊,

如果內容有錯誤的在麻煩各位大神指正。

 

參考

https://docs.microsoft.com/zh-tw/aspnet/core/mvc/models/validation?view=aspnetcore-2.1

http://www.binaryintellect.net/articles/d80b2b90-847b-4c5b-90ac-c2db18e131be.aspx