首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET MaxLengthValidator:支援中英混排位元組長度驗證的自訂驗證控制項

     編輯:ASP.NET 基礎

為什麼需要這個驗證控制項?

ASP.NET 內建 MaxLength 屬性僅限制字元數,但資料庫 varchar(n) 欄位限制的是位元組數。中文字在 UTF-8 佔 3 bytes、Big5/GB2312 佔 2 bytes,英文數字佔 1 byte。直接存入會導致截斷或例外。MaxLengthValidator 解決此問題:雙端驗證(伺服器+客戶端),支援非 Unicode 欄位位元組長度計算。

核心邏輯:位元組長度計算

/// 
/// 計算字串在非 Unicode 欄位 (varchar/char) 的位元組長度
/// 中文字元計 2 bytes,英數字元計 1 byte
/// 
public virtual int GetLengthForNonNationnal(string text)
{
    if (string.IsNullOrEmpty(text)) return 0;
    
    int length = 0;
    foreach (char c in text)
    {
        length++;
        if (c > 255) length++;  // 非 ASCII 字元視為雙位元組
    }
    return length;
}

完整控制項實作

[Description("對 MaxLength 進行驗證")]
[ToolboxData("<{0}:MaxLengthValidator runat=server></{0}:MaxLengthValidator>")]
public class MaxLengthValidator : BaseValidator
{
    #region 屬性
    [DefaultValue(true)]
    public bool ValidateForNonNationalField
    {
        get => ViewState["ValidateForNonNationalField"] as bool? ?? true;
        set => ViewState["ValidateForNonNationalField"] = value;
    }
    #endregion

    #region 伺服器端驗證
    protected override bool EvaluateIsValid()
    {
        var target = NamingContainer.FindControl(ControlToValidate) as TextBox;
        if (target == null) return true;

        int maxLength = target.MaxLength;
        string value = GetControlValidationValue(ControlToValidate);
        if (string.IsNullOrEmpty(value)) return true;

        int actualLength = ValidateForNonNationalField 
            ? GetLengthForNonNationnal(value) 
            : value.Length;

        return actualLength <= maxLength;
    }
    #endregion

    #region 客戶端腳本註冊
    protected override void OnPreRender(EventArgs e)
    {
        string script = @"
function MaxLengthValidator_GetNonNationalLength(text) {
    if (!text) return 0;
    var length = 0;
    for (var i = 0; i < text.length; i++) {
        length++;
        if (text.charCodeAt(i) > 255) length++;
    }
    return length;
}

function MaxLengthValidator_Validate(val) {
    if (!val || !val.TargetControlID) return true;
    var target = document.getElementById(val.TargetControlID);
    if (!target) return true;
    
    var maxLength = parseInt(val.maxlength, 10);
    if (isNaN(maxLength)) return true;
    
    var value = target.value;
    if (!value) return true;
    
    var realLength = val.ValidateForNonNationalField === 'true' 
        ? MaxLengthValidator_GetNonNationalLength(value) 
        : value.length;
    
    return realLength <= maxLength;
}";

        ScriptManager.RegisterClientScriptBlock(this, GetType(), 
            "MaxLengthValidator_Validate", script, true);
        
        base.OnPreRender(e);
    }
    #endregion

    #region 渲染屬性傳遞給客戶端
    protected override void AddAttributesToRender(HtmlTextWriter writer)
    {
        base.AddAttributesToRender(writer);
        if (RenderUplevel)
        {
            var target = NamingContainer.FindControl(ControlToValidate) as TextBox;
            writer.AddAttribute("TargetControlID", GetControlRenderID(ControlToValidate));
            writer.AddAttribute("ValidateForNonNationalField", ValidateForNonNationalField ? "true" : "false");
            writer.AddAttribute("evaluationfunction", "MaxLengthValidator_Validate");
            if (target != null) writer.AddAttribute("maxlength", target.MaxLength.ToString());
        }
    }
    #endregion
}

使用範例

<%@ Register Assembly="YourAssembly" Namespace="YourNamespace" TagPrefix="cc" %>

<asp:TextBox ID="txtTitle" runat="server" MaxLength="100" Columns="50" />
<cc:MaxLengthValidator 
    ID="valTitle" 
    runat="server" 
    ControlToValidate="txtTitle" 
    ValidateForNonNationalField="true" 
    ErrorMessage="標題長度超過限制 (中文算 2 個字元)" 
    Display="Dynamic"
    CssClass="text-danger" />

資料庫欄位對應建議

資料庫欄位ValidateForNonNationalField說明
varchar(n) / char(n)true非 Unicode,中文佔 2 bytes (Big5/GBK) 或 3 bytes (UTF-8)
nvarchar(n) / nchar(n)falseUnicode,每字元固定 2 bytes (UTF-16)
text / ntextfalse通常不限長度,建議不驗證

進階:支援 UTF-8 精確位元組計算

若資料庫使用 UTF-8 編碼,中文佔 3 bytes,可繼承並覆寫計算方法:

public class Utf8MaxLengthValidator : MaxLengthValidator
{
    public override int GetLengthForNonNationnal(string text)
    {
        if (string.IsNullOrEmpty(text)) return 0;
        return Encoding.UTF8.GetByteCount(text); // 精確 UTF-8 位元組數
    }
}

最佳實踐

  1. 雙重保障:TextBox 設 MaxLength (字元數上限) + Validator 驗證位元組長度
  2. 錯誤訊息明確:告知使用者「中文算 2 個字元」避免困惑
  3. 效能考量:客戶端腳本僅註冊一次,ScriptManager.RegisterClientScriptBlock 自動去重
  4. 整合驗證群組:設定 ValidationGroup 配合送出按鈕

總結

MaxLengthValidator 以 30 行核心邏輯 解決了 ASP.NET 開發中極常見卻常被忽視的「字元數 vs 位元組數」不匹配問題。其設計亮點:繼承 BaseValidator 获得完整驗證生命週期、雙端同步邏輯避免繁瑕維護、可配置的計算模式適應不同資料庫編碼。建議納入專案共用控制項庫,所有涉及 varchar 欄位的表單統一套用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved