7-2.ASP.NET Core MVC 入門教學 - 自訂驗證標籤

ASP.NET Core MVC 入門教學

在新增的章節有提到過官方有內建一些常用的驗證標籤供我們使用,但有時候還是會碰上想在自己做一個更適合自己使用的驗證標籤,這時候就可以使用繼承ValidationAttribute來打造專屬自己的驗證標籤。

首先先創一個ValidationAttributes資料夾來放自訂的驗證標籤,然後我們取名為NewsTitleAttribute

接著寫上我們的基本外框,繼承ValidationAttribute

public class NewsTitleAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
    }
}

繼承ValidationAttribute,並且裡面有一個override ValidationResult的方法,這是我們今天要寫判斷邏輯的地方。

接著要來示範,如果我今天想從資料庫驗證,新聞標題有沒有存在,那該如何實作呢,因為大部分的判斷可以用內建的正則表達式解決

所以示範一個沒辦法用規則解決的範例,就是讀取資料庫,那我們可以透過ValidationContext來取得相關service,這邊我們是取得資料庫物件

KcgContext _kcgContext = (KcgContext)validationContext.GetService(typeof(KcgContext));

接著我們就來寫相關判斷邏輯,以下為完整程式碼。

public class NewsTitleAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        KcgContext _kcgContext = (KcgContext)validationContext.GetService(typeof(KcgContext));

        var title = (string)value;

        var findTitle = from a in _kcgContext.News
                        where a.Title == title
                        select a;

        if (findTitle.FirstOrDefault() != null)
        {
            return new ValidationResult("已存在相同的新聞標題");
        }

        return ValidationResult.Success;
    }
}

一開始我們從object value取到的值會是你驗證標籤所放置的欄位的值,因為我們預定會放在title上,所以會是個string,接著我們再去資料庫撈有沒有相同的title,最後如果findTitle != null就代表資料庫有相同的title,那我們就回傳一個錯誤訊息,如果找不到則會執行ValidationResult.Success代表我們通過驗證。

接著我們把寫好的標籤放在NewsCreateDto.cs的Title上方

public class NewsCreateDto
{
    [NewsTitle]
    [Required(ErrorMessage = "標題不可為空")]
    public string Title { get; set; }

}

然後我們馬上來測試效果如何,新增一筆資料庫已存在的新聞標題的資料

這邊就可以看到出現提示"已存在相同的新聞標題",代表驗證程式有如期的運作。

但這時候如果我們要放在同樣要驗證標題是否重複的NewsEditDto.cs的Title上可能就會有一些小問題,什麼樣的問題?就是我們今天可能在做修改時,Name並不改動而是改其他欄位,那我們上方的判斷邏輯,就會抓到自己這筆而導致驗證不過,所以我們要修改一下。

如上圖,我們這樣的邏輯會變成自己跟自己重複,所以今天我們要排除掉自己


public class NewsTitleAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        KcgContext _kcgContext = (KcgContext)validationContext.GetService(typeof(KcgContext));

        var title = (string)value;

        var findTitle = from a in _kcgContext.News
                        where a.Title == title
                        select a;

        var dto = validationContext.ObjectInstance;

        if (dto.GetType() == typeof(NewsEditDto))
        {
            var updateDto = (NewsEditDto)dto;
            findTitle = findTitle.Where(a => a.NewsId != updateDto.NewsId);
        }

        if (findTitle.FirstOrDefault() != null)
        {
            return new ValidationResult("已存在相同的新聞標題");
        }

        return ValidationResult.Success;
    }
}

一開始我們先從validationContext.ObjectInstance可取得整個類別,接著我們再判斷我們是進到哪個類別,這邊的話就是如果是dto.GetType() == typeof(NewsEditDto),代表我們當下是做編輯,這裡也可以表現出為什麼我們必須將新增和修改拆成兩個Dto的原因之一,因為我們可能會有很多判斷是不一樣的。

確定是更新的時候,我們就再排除自己的NewsId,findTitle = findTitle.Where(a => a.NewsId != updateDto.NewsId);,這樣就大功告成了。

這樣我們就可以正常編輯了

這個文章學到了兩個特殊用法,第一個就是取得資料庫物件(service)的方法validationContext.GetService

第二個就是判斷進入時類別的方法,var dto = validationContext.ObjectInstance;dto.GetType() == typeof(NewsEditDto)

 

範例檔:下載




Copyright © 凱哥寫程式 2022 | Powered by TalllKai ❤