熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

服務端數據校驗及客戶端js腳本驗證集成處理初探

2013-11-13 10:30:00  來源: .NET編程 

  這篇文章發出來有段時間了不過看的人不多建議的人也少! 今天在首頁看到有朋友把驗證方案發到首頁去了為了賺些評論我也在這邊編輯下發到首頁上給大家扔些磚!
起源

  在項目開發中數據有效性驗證肯定是必須的那麼在哪裡驗證呢!?怎麼去驗證呢?      針對web項目而言客戶端驗證+服務端的驗證缺一不可客戶端的腳本驗證用於提高用戶體驗!   服務端的驗證主要是為了數據的安全性合法性的驗證! 但是我們在實施這兩種驗證的時候會發現有以下幾個可能出現的問題

  客戶端代碼驗證寫起來相對比較煩瑣而且易出錯!(主要是拼寫錯誤例如js方法名拼寫錯誤)

   服務端的驗證和客戶端驗證要保持一致性一樣很煩瑣(客戶驗證輸入不能超過個字符那後服務器端也要相應的控制在個字符而且修改時需要將客戶端和服務端同步修改提示信息前後端都要再輸入一次!一個字總覺得時間不應該花在這個上面!

  驗證與賦值一般情況下客戶端提交的數據我們驗證通過後把變量的值賦給實體或是相關的變量如果不通過有些時候我們有時候還會需要給變量或實體屬性值加上一個默認值這個時候我們寫的代碼可能不避免的出現一些if的判斷語句讓代碼顯得不夠簡潔

  其它的細節問題這裡就不一一列舉了我針對上面的問題自己做了一個下面的小組件!

  二 思路說明

  為了解決第一個問題和第二個問題我決定客戶端的驗證代碼由服務端的驗證代碼直接生成頁面中這樣可以避免客戶端的代碼在編寫上的錯誤!而服務端的程序代碼每次被訪問時通過驗證狀態開關來開啟驗證用戶可以根據自己的業務邏輯來開啟驗證我先定義了一個驗證容器ValidatorContainer和一個驗證器Validator兩個概念驗證容器是指包括了一個或多個驗證器對象的容器類驗證器是針對每一條數據項所添加的驗證信息類!

  三調用說明

  可能這一部分的內容可以讓你更直觀的了解這個驗證組件的一些原理和信息

  第一步先添加引用這個不用多說Validatordll引用進去(最下面附有項目的源碼)

  第二步代碼都在下面了不用再說了

  cs頁面的代碼:

  using Validator;
using MyFrameworkUtility;
public partial class _Default : SystemWebUIPage
{
    protected ValidatorContainer vc;
    protected void Page_Load(object sender EventArgs e)
    {
        //是否開啟服務端驗證
        bool isStartCheck = false;
        if (Request[command] == postBack)
        {
            /*
             * 此處的bool值主要控制驗證容器處在何種狀態
             * 狀態false只注冊客戶端代碼不實施服務端的驗證
             * 狀態true驗證客戶端的數據並生成客戶端代碼字符串
             * */
            isStartCheck = true;
        }

  //初始化驗證容器的集合並傳入isStartCheck來控制驗證的開啟
        vc = new ValidatorContainer(isStartCheck);

  //設置當前的驗證組(些處可以根據你自己的頁面邏輯來選擇驗證不設置為驗證所有)
        vcGroupName = A;

  //獲取並驗證客戶端的數據
        int month = vcAdd(Validator<int>Init(MyRequestGetInt(monthTextBox)))//注意這一行我們需要把驗證器放置到對應的容器中再進行驗證
            Integer(月份必須為的整數)
            Range(月份必須在月之間)//驗證數字和范圍
            SetGroupName(A)//設置其所屬的驗證組可以不設置(不設置會被分配到default組)
            RegClientValidator(monthTextBox 請輸入出生月 monthErrorSpan)//將驗證代碼注冊到客戶端頁面(也可以選擇不注冊)
            //參數說明參數monthTextBox表示html控件的id[參數]請輸入出生月表示提示Div中的文字(該參數為重載)[參數]monthErrorSpan表示錯誤信息所顯示的框(該參數為重載)
            Retrun();//返回驗證通過後的值否則返回默認值(此處返回的是);

  string userName = vcAdd(Validator<string>Init(MyRequestGetString(userNameTextBox)))
            Chinese(用戶名必須為中文!)
            Length(用戶名必須在個字以內!)
            SetGroupName(A)
            RegClientValidator(userNameTextBox)
            Retrun(給你個默認值);

  string rePostDateString = vcAdd(Validator<string>Init(MyRequestGetString(rePostDateTextBox)))
            Date(日期格式不正確)
            Range(DateTimeNow DateTimeMaxValue 重發日期不能少於當前日期!)//這個功能客戶端的還沒寫
            SetGroupName(A)
            IsRequired(false)//設置其為非必填項
            RegClientValidator(rePostDateTextBox這個是非必填項當然你如果要填的話我肯定是要驗證的啦!)
            Retrun();

  //注冊客戶端代碼到頁面中這裡必須要聲明運行於服務端的Header或是Form才能進行注冊
        vcRenderClinetValidator();

  //判斷驗證是否通過
        if (isStartCheck)
        {
            if (vcIsAllPass)
            {
                //你頁面要做的邏輯
            }
        }
    }
}

  aspx頁面的調用代碼:

  <head>
<!這裡加runat=server或是如現在的頁面有一個 <form id=form runat=server>是runat=server>
    <title>驗證器測試頁</title>
</head>
<body >
    <form id=form runat=server>
    <div>
    <br />
        請輸入出生月份<input type=text id=monthTextBox value=aa /><span id=monthErrorSpan >這裡將用來顯示驗證反饋信息</span><br />
        請輸入用戶昵稱<input type=text id=userNameTextBox value=aa /><br />
        更新日期<input type=text id=rePostDateTextBox value=aa /><br />
        <input type=button id= value=統一驗證 onclick=<%=vcChecked(A)%> />
<!上面這個地方就是調用的地方<%=vcChecked(A)%>會產生一個js調用函數

  A代表要驗證的組產生的這個調用的函數執行完會返回一個表單是否驗證通過的bool值 >

  </div>
    </form>
</body>
</html>

  上面這段代碼就實現了客戶端驗證代碼的注冊及調用應該來說還是比較簡單的!

  上面的調用上都寫了相關的注釋如果有不明確的地方接著往下看

  //是否開啟服務端驗證
        bool isStartCheck = false;
        if (Request[command] == postBack)
        {
            /*
             * 此處的bool值主要控制驗證容器處在何種狀態
             * 狀態false只注冊客戶端代碼不實施服務端的驗證
             * 狀態true驗證客戶端的數據並生成客戶端代碼字符串
             * */
            isStartCheck = true;
        }

  //初始化驗證容器的集合並傳入isStartCheck來控制驗證的開啟
        vc = new ValidatorContainer(isStartCheck);

  //設置當前的驗證組(些處可以根據你自己的頁面邏輯來選擇驗證不設置為驗證所有)
        vcGroupName = A;

  上面這段代碼裡面是初始化開關條件變量以及初始化驗證器以及驗證的組名這個地方組名其實是非必須的如查不添加這個組名組件會自動分

  配到Default組中在new  ValidatorContainer(isStartCheck)中的isStartCheck是用為啟動驗證的如果這個值是false的話那麼

  ValidatorContainer就只會生成客戶端代碼而不執行數據有效性驗證!

  接下來再看下面的代碼

  //獲取並驗證客戶端的數據
        int month = vcAdd(Validator<int>Init(MyRequestGetInt(monthTextBox)))//注意這一行我們需要把驗證器放置到對應的容器中再進行驗證
            Integer(月份必須為的整數)
            Range(月份必須在月之間)//驗證數字和范圍
            SetGroupName(A)//設置其所屬的驗證組可以不設置(不設置會被分配到default組)
            RegClientValidator(monthTextBox 請輸入出生月 monthErrorSpan)//將驗證代碼注冊到客戶端頁面(也可以選擇不注冊)
            //參數說明參數monthTextBox表示html控件的id[參數]請輸入出生月表示提示Div中的文字(該參數為重載)[參數]monthErrorSpan表示錯誤信息所顯示的框(該參數為重載)
            Retrun();//返回驗證通過後的值否則返回默認值(此處返回的是);

  這個裡面我們可以看出來我們用vcAdd(Validator<T> v)方法來往驗證器裡面添加驗證器特別注意的地方是

  Validator本身具備了IntegerRange 等方法但是我們這裡不可以直接調用如果直接調用在這裡它的驗證結果將不能被vc容監控到!

  所以要特別注意

  Validator<int>Init(MyRequestGetInt(monthTextBox))

  這小段代碼是為MyRequestGetInt(monthTextBox)這個整數做一個校驗這個地方這個方法是另一個組件裡的大家應該看得明白

  來獲取客戶端的一個提交值的如果不為整數的話默認為這個地方有點多余因為驗證器裡本來就有一個針對整數的驗證了這個我們就先不

  管它了反正我這個組件的思路是不相干的!

  後面調用的IntegerRange 等方法就不多做解釋了它們只是一個驗證方法而已關鍵RegClientValidator方法這個方法有三個重載版本

  RegClientValidator(string elementName);這個表示為html頁面中id或是name為elementName的html控件添加js驗證的腳本代碼(注:這一步其實只做了一個腳本代碼的生成並沒有輸出)

  RegClientValidator(string elementNamestring tipString);這個重載表示為頁面控件添加驗證並在輸入時添加提示信息提示信息的位置現在是

  默認的在控件的上方(具體效果如圖我會在以後的代碼更新中會加入一個可以自定義顯示的位置給用靈活的配置或是使用如圖的默認)

  
如果不使用這個重載自然沒有這個提示信息了!

  然後講第三個重載:RegClientValidator(string elementNamestring tipStringstring errspanName)

  這個重載前兩上參數都講過了第三個參數其實是控制是否在自定義的html元素裡面顯示錯誤提示信息

  我代碼中monthErrorSpan其實就是圖上紅色的那塊區域的html元素的id這樣如果驗證不通過信息將在紅色區域中顯示如果沒有使用這個重載

  我們會自動生成一個div來顯示效果如下圖:

  

  注這個錯誤提示的內容就是在cs文件裡面寫的那些字符串在哪一下的驗證中不通過就會顯示相應的錯誤提示!

  接下來看Retrurn(T tValue);

  這個方法其實就是用來返回值的意思就是如果驗證通過返回用戶提交的數據給接收變量如果不通過將Retrun(T tValue)中定義的值賦給接收變量這個應該比較好理解如果你沒有定義接收變量這個方法也可以省略~

  然後再看這兩個方法

  SetGroupName(A)
IsRequired(false)//設置其為非必填項

  上面那個SetGroupName(A)表示把該驗證添加到A組中其實這個地方我要聲明的是一般%甚至更多的頁面的驗證是不需要分組的當然這個也用不著我在這個地方只是為以演示這個分組功能才加上去的分組驗證的情況有用過的朋友估計會明白我這個地方就不多說了不需要分組的就不用調用這個方法咯!

  下面這個IsRequired(false)這個字面上應該也很好理解就是設置成非必填項當然如果用戶輸入了的話還是會有驗證的!

  好了上面對該解釋的都解釋了現在看最後的幾代碼

  //注冊客戶端代碼到頁面中這裡必須要聲明運行於服務端的Header或是Form才能進行注冊
        vcRenderClinetValidator();

  //判斷驗證是否通過
        if (isStartCheck)
        {
            if (vcIsAllPass)
            {
                //你頁面要做的邏輯
            }
        }
 vcRenderClientValidator();這個方法做的工作是客戶端驗證生成的最後一步它負責把容器裡所有的客戶端驗證代碼生成後寫入到html代碼中~!

  有多關鍵不用我說了吧!

  下面vcIsAllPass這個也不用說了就是容器裡的驗證是否都通過了!

  差不多了剩下的代碼就是前端的調用這個地方有些不合理我估計在下一個更新中會處理掉!

  <input type=button id= value=統一驗證 onclick=<%=vcChecked(A)%> />

  客戶端的代碼生成後在什麼地方激活怎麼調用要看客戶的需求了這個地方需要調用vcChecked(string groupName)來生成js調用代碼!

  沒有這一步的話程序只會為失去焦點做驗證提交的時候當然就不會做了!

  說了蠻多的不過感覺就是沒說清楚!

  具體的實現我挑一兩個方法貼出來更詳細的就下載下面的源碼吧!最近一直沒時間改這個寫了個把月了一起沒有把現在有的問題更新!

  不哆嗦了看代碼:

  驗證器的其中一個驗證項方法:

  #region 驗證數字(intdouble)
        /// <summary>
        /// 驗證數字(intdouble)
        /// </summary>
        /// <param name=errorMessage>錯誤提示字符串</param>
        /// <returns></returns>
        public Validator<T> Double(string errorMessage)
        {
            tempJsonString = new StringBuilder();
            tempJsonStringAppend(Double:{);
            tempJsonStringAppend(ConvertToJsonElement(Msg errorMessage));
            tempJsonStringAppend(});
            jsonListAdd(tempJsonStringToString());
            //判斷是否需要忽略驗證
            if (ignore)
                return this;
            //驗證
            if (!IsPattern(validatorRegexs[]))
            {
                thisReject(errorMessage);
            }
            return this;
        }

  #endregion

  驗證容器的

  Code
using System;
using SystemCollectionsGeneric;
using SystemText;
using Validator;
using SystemWeb;
using SystemWebUI;
using SystemWebUIWebControls;
using SystemSecurityPermissions;

  namespace Validator
{
    [AspNetHostingPermission(SecurityActionDemand Level = AspNetHostingPermissionLevelMinimal)]
    public class ValidatorContainer : IObserver
    {
        /// <summary>
        /// 是否開啟驗證
        /// </summary>
        private bool isStartCheck;

  /// <summary>
        /// 驗證器所在頁對象
        /// </summary>
        private Page cPage;

  /// <summary>
        /// 客戶端驗證器對象名
        /// </summary>
        private string clientValidatorName;

  /// <summary>
        /// 驗證組名
        /// </summary>
        private string groupName;

  /// <summary>
        /// 錯誤數量
        /// </summary>
        //private int errorCount;

  /// <summary>
        /// 前台呈現腳本代碼
        /// </summary>
        private StringBuilder renderString;

  /// <summary>
        /// 驗證器列表
        /// </summary>
        private List<ISubject> validatorList;

  /// <summary>
        /// 是否開啟驗證
        /// </summary>
        public bool IsStartCheck
        {
            get { return isStartCheck; }
            set { isStartCheck = value; }
        }

  /// <summary>
        /// 客戶端驗證器對象名
        /// </summary>
        public string ClientValidatorName
        {
            get { return clientValidatorName; }
            set { clientValidatorName = value; }
        }

  /// <summary>
        /// 驗證組名
        /// </summary>
        public string GroupName
        {
            get { return groupName; }
            set { groupName = value; }
        }

  #region 是否通過驗證
        /// <summary>
        /// 是否通過驗證
        /// </summary>
        public bool IsAllPass
        {
            get { return GetPassStat(); }
        }

  /// <summary>
        /// 獲取驗證狀態
        /// </summary>
        /// <returns></returns>
        private bool GetPassStat()
        {
            foreach (ISubject o in validatorList)
            {
                if (!oIsPass)
                    return false;
            }
            return true;
        }
        #endregion

  /// <summary>
        /// 是否開啟驗證
        /// </summary>
        /// <param name=_isStartCheck></param>
        public ValidatorContainer(bool _isStartCheck)
        {
            validatorList = new List<ISubject>();
            clientValidatorName = Validator;
            thisisStartCheck = _isStartCheck;
        }

  /// <summary>
        /// 驗證器列表
        /// </summary>
        public List<ISubject> ValidatorList
        {
            get { return validatorList; }
            set { validatorList = value; }
        }

  /// <summary>
        /// 添加字符串驗證器
        /// </summary>
        /// <param name=_validator>字符串驗證器對象</param>
        public ValidatorValidator<string> Add(ValidatorValidator<string> _validator)
        {
            if (thisgroupName != _All)
                if (thisgroupName != _validatorGroupName)
                    _validatorIgnore = true;
            validatorListAdd(_validator);
            return _validator;
        }

  /// <summary>
        /// 添加整數驗證器
        /// </summary>
        /// <param name=_validator>整數驗證器對象</param>
        public ValidatorValidator<int> Add(ValidatorValidator<int> _validator)
        {
            if (thisgroupName != _All)
                if (thisgroupName != _validatorGroupName)
                    _validatorIgnore = true;
            validatorListAdd(_validator);
            return _validator;
        }

  /// <summary>
        /// 獲取所有分組的驗證不通過數量
        /// </summary>
        /// <returns></returns>
        public int GetErrorCount()
        {
            return ;
        }

  /// <summary>
        /// 獲取某分組驗證不能過數目
        /// </summary>
        /// <param name=_groupName></param>
        /// <returns></returns>
        public int GetErrorCount(string _groupName)
        {
            return ;
        }

  #region 實現觀察者接口
        public void GetValidatorStat(ISubject subject)
        {
            //根據subject中的信息記錄到匯總信息中
            //subjectGroupName;
            //subjectIgnore;
            //subjectIsPass;
            //subjectErrorMessage;
        }
        #endregion

  /// <summary>
        /// 生成客戶端驗證json代碼
        /// </summary>
        /// <returns></returns>
        public void RenderClinetValidator()
        {
            RenderClinetValidator(null);
        }

  /// <summary>
        /// 是否存在頁面注冊區域
        /// </summary>
        /// <returns>存在的區域枚舉</returns>
        private FormOrHeader GetRegisterRegion()
        {
            cPage = (Page)HttpContextCurrentHandler;
            if (cPageHeader != null)
            {
                return FormOrHeaderHeader;
            }
            else if (cPageForm != null)
            {
                return FormOrHeaderForm;
            }
            return FormOrHeaderNull;
        }

  /// <summary>
        /// 生成客戶端驗證json代碼
        /// </summary>
        /// <param name=_clientValidatorName>客戶端驗證器名稱</param>
        /// <returns></returns>
        public void RenderClinetValidator(string _clientValidatorName)
        {
            FormOrHeader formOrHeader = GetRegisterRegion();
            if (formOrHeader == FormOrHeaderNull)
                throw new Exception(對不起頁面不存在必須存在標記為\runat=server\的Form或是Header標記!請確認!);

  //設置客戶端驗證對象名
            if (!stringIsNullOrEmpty(_clientValidatorName))
                thisclientValidatorName = _clientValidatorName;

  //按組名排序
            validatorListSort(new GroupComparer());

  renderString = new StringBuilder();
            renderStringAppendLine(<script type=text/javascript>);
            renderStringAppendLine(var + clientValidatorName + ;);
            renderStringAppendLine(function InitValidator(){);
            renderStringAppendLine(clientValidatorName + =new MyValidator( + clientValidatorName + Container););
            renderStringAppendLine(});
            renderStringAppendLine(BindEvent(windowonloadInitValidator););
            renderStringAppendLine(var + clientValidatorName + Container={);
            int i = ;
            string currentGroupName;
            string preGroupName;
            string nextGroupName;
            int totalCount = validatorListCount;
            foreach (ISubject o in validatorList)
            {
                //設置組名
                currentGroupName = validatorList[i]GroupName;
                preGroupName = validatorList[(i ) < ? : (i )]GroupName;
                nextGroupName = validatorList[(i + ) > totalCount ? totalCount : (i + )]GroupName;
                if (i == )
                    renderStringAppend(currentGroupName + :{);
                if (currentGroupName != preGroupName)
                {
                    renderStringAppend(});
                    renderStringAppend( + currentGroupName + :{);
                }

  i++;
                renderStringAppend(oJsonString);
                if (i != validatorListCount)
                    if (currentGroupName == nextGroupName)
                        renderStringAppend(oJsonString == stringEmpty ? : );
            }
            renderStringAppend(});
            renderStringAppendLine(};);
            renderStringAppendLine(</script>);

  //加載圖片
            string imageTemplate = <img id=falseIcon src={} style=display:none>;
            LiteralControl imageUrlLiterl = new LiteralControl(stringFormat(imageTemplate cPageClientScriptGetWebResourceUrl(thisGetType() ValidatorResourcesiconsgif)));
            cPagePageControlsAddAt( imageUrlLiterl);
            //裝載樣式
            string includeTemplate = <link rel=stylesheet text=text/css />;
            string includeLocation = cPageClientScriptGetWebResourceUrl(thisGetType() ValidatorResourcesvalidatorcss);
            LiteralControl includeLocationLiteral = new LiteralControl(StringFormat(includeTemplate includeLocation));
            //裝載js
            string jsTemplate = <script src={} type=text/javascript></script>;
            string scriptLocation = cPageClientScriptGetWebResourceUrl(thisGetType() ValidatorResourcesMyValidatorjs);
            LiteralControl scriptLocationLiteral = new LiteralControl(StringFormat(jsTemplate scriptLocation));

  if (formOrHeader == FormOrHeaderHeader)
            {
                cPageHeaderControlsAdd(includeLocationLiteral);
                cPageHeaderControlsAdd(scriptLocationLiteral);
                cPageHeaderControlsAdd(new LiteralControl(renderStringToString()));
            }
            else
            {
                cPageControlsAddAt( includeLocationLiteral);
                cPageControlsAddAt( scriptLocationLiteral);
                cPageClientScriptRegisterClientScriptBlock(cPageGetType() thisclientValidatorName renderStringToString());
            }
        }

  /// <summary>
        /// 驗證所有組
        /// </summary>
        /// <returns>組驗證調用函數</returns>
        public string Checked()
        {
            return Checked(_All);
        }

  /// <summary>
        /// 驗證特定組
        /// </summary>
        /// <param name=groupName>組名(_All表示驗證所有組)</param>
        /// <returns>組驗證調用函數</returns>
        public string Checked(string _groupName)
        {
            string checkGroup = _All;
            if (!stringIsNullOrEmpty(_groupName))
                checkGroup = _groupName;
            return clientValidatorName + ValidateByGroup( + checkGroup + );;
        }
    }

  /// <summary>
    /// Form或Header
    /// </summary>
    public enum FormOrHeader : byte
    {
        /// <summary>
        /// 頭部
        /// </summary>
        Header
        /// <summary>
        /// 表單
        /// </summary>
        Form
        /// <summary>
        /// 無
        /// </summary>
        Null
    }
}

  集成在項目中的js的代碼我也不貼了這些js的引用及css的引用程序都包辦了

  調用肯定只要引用Validatordll就可以了!

  代碼質量不高想到哪寫到哪一直沒有時間整理到時整理出來完善版的再發上來希望大家給予批評!

  真誠的感謝您抽時間來看我的文章!

  謝了!

  源碼發上來有興趣的可以下去看看(注:TestWeb是上面這個測試頁面的站點另外一個大家自己猜是什麼好了!)


From:http://tw.wingwit.com/Article/program/net/201311/13829.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.