iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 16
0
Modern Web

菜鳥練等區-ASP.Net Core MVC進化之路系列 第 16

[鐵人賽Day16] - Model Validation(2) / 自訂及遠端驗證

前言

本文將延續上一篇還沒講完的Model Validation
繼續介紹ASP.Net Core MVC自訂及遠端驗證的使用方式。

同步發表於個人點部落 - [鐵人賽Day16] ASP.Net Core MVC 進化之路 - Model Validation(2) / 自訂及遠端驗證

自訂驗證

雖然預設已有許多實用的[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)是不能夠重複的,
我們這部分透過遠端驗證來實現。

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


上一篇
[鐵人賽Day15] - Model Validation(1) / 前端 vs. 後端驗證
下一篇
[鐵人賽Day17] - Filter
系列文
菜鳥練等區-ASP.Net Core MVC進化之路30

尚未有邦友留言

立即登入留言