Table of Contents

當內建的驗證器無法滿足您的需求時,您可以建立自訂的驗證器。

方法 1:撰寫新的 FormValidator

實作 IFormValidator 介面來建立自訂驗證器。

實作介面

using CloudyWing.FormValidators;

public class CustomFormValidator : IFormValidator {
    public CustomFormValidator(string column, string value) {
        Column = column;
        Value = value;
    }

    public string Column { get; }

    public string Value { get; }

    public string ErrorMessage { get; private set; }

    public bool IsValid { get; private set; }

    public bool Validate() {
        // 實作您的驗證邏輯
        if (string.IsNullOrWhiteSpace(Value)) {
            IsValid = true; // 空值視為通過(遵循一般驗證器慣例)
            return true;
        }

        // 您的驗證邏輯
        bool isValid = YourValidationLogic(Value);

        if (!isValid) {
            ErrorMessage = $"{Column} 驗證失敗。";
        }

        IsValid = isValid;
        return isValid;
    }

    private bool YourValidationLogic(string value) {
        // 在此撰寫您的驗證邏輯
        return true;
    }
}

使用自訂驗證器

BulkValidator validators = new();
validators.Add(new CustomFormValidator("欄位名稱", "欄位值"));

if (!validators.Validate()) {
    Console.WriteLine(validators.ErrorMessage);
}

方法 2:撰寫 ValidationProvider 的擴充方法

為了讓自訂驗證器也能使用 Fluent API 語法,您可以建立擴充方法。

建立擴充方法

using CloudyWing.FormValidators.Core;

public static class ValidationProviderExtensions {
    public static Func<string, string, CustomFormValidator> Custom(this ValidationProvider provider)
        => (column, value) => new CustomFormValidator(column, value);
}

使用擴充方法

BulkValidator validators = new(cfg => {
    cfg.Add("欄位名稱", "欄位值", 
        opt => opt.Required(),
        opt => opt.Custom()
    ); // 使用自訂驗證器
});

if (!validators.Validate()) {
    Console.WriteLine(validators.ErrorMessage);
}

實際範例:URL Slug 驗證器

以下是一個實際的範例,驗證字串是否為有效的 URL slug(如 my-blog-post)。

驗證器實作

using System.Text.RegularExpressions;
using CloudyWing.FormValidators;

public class UrlSlugValidator : IFormValidator {
    private static readonly Regex SlugRegex = new(
        @"^[a-z0-9]+(?:-[a-z0-9]+)*$",
        RegexOptions.Compiled
    );

    public UrlSlugValidator(string column, string value) {
        Column = column;
        Value = value;
    }

    public string Column { get; }

    public string Value { get; }

    public string ErrorMessage { get; private set; }

    public bool IsValid { get; private set; }

    public bool Validate() {
        if (string.IsNullOrWhiteSpace(Value)) {
            IsValid = true;
            return true;
        }

        bool isValid = SlugRegex.IsMatch(Value);

        if (!isValid) {
            ErrorMessage = $"{Column} 必須為小寫字母、數字和連字號組成,且不能以連字號開頭或結尾。";
        }

        IsValid = isValid;
        return isValid;
    }
}

擴充方法

using CloudyWing.FormValidators.Core;

public static class CustomValidationProviderExtensions {
    public static Func<string, string, UrlSlugValidator> UrlSlug(this ValidationProvider provider)
        => (column, value) => new UrlSlugValidator(column, value);
}

使用範例

BulkValidator validators = new(cfg => {
    cfg.Add("文章網址", articleSlug,
        opt => opt.Required(),
        opt => opt.UrlSlug()
    );
});

if (!validators.Validate()) {
    // 輸出:「文章網址 必須為小寫字母、數字和連字號組成,且不能以連字號開頭或結尾。」
    Console.WriteLine(validators.ErrorMessage);
}

最佳實踐

1. 遵循空值慣例

大多數內建驗證器在遇到空值時會回傳 true(除了 RequiredValidator)。建議您的自訂驗證器也遵循這個慣例:

public bool Validate() {
    if (string.IsNullOrWhiteSpace(Value)) {
        IsValid = true;
        return true; // 空值視為通過
    }
    
    // 您的驗證邏輯...
}

2. 提供有意義的錯誤訊息

錯誤訊息應明確告知使用者問題所在:

// ✗ 不良範例
ErrorMessage = $"{Column} 無效。";

// ✓ 良好範例
ErrorMessage = $"{Column} 必須為 8-20 位英數字元,且至少包含一個大寫字母。";

3. 考慮效能

如果驗證邏輯複雜,考慮使用快取或預編譯:

// 使用 static readonly 預編譯正規表示式
private static readonly Regex Pattern = new(
    @"^pattern$",
    RegexOptions.Compiled
);

4. 支援參數化

讓驗證器更靈活:

public class RangeValidator : IFormValidator {
    private readonly int min;
    private readonly int max;

    public RangeValidator(string column, string value, int min, int max) {
        Column = column;
        Value = value;
        this.min = min;
        this.max = max;
    }

    // ... 實作驗證邏輯
}

相關資源