后台參數驗證的幾種方式


前台和后台驗證(MVC、Struts2)的必要性經驗總結:

1.后端驗證是必需的,只有后端驗證才能保證表單數據輸入的合法性,前端驗證的主要目的是為了方便用戶,增強用戶體驗。
2.雖然不是必需的,但目前也算是一種發展趨勢,特別是面向一般用戶的網站,沒有加前端驗證可能會加大用戶注冊跑路率。
3.前端驗證方式:
1)目前主流的Web框架已經集成了前后端驗證功能,如:Asp.net mvc,PHP 的Yii 等,只要定好驗證規則,前端驗證代碼就自動生成好,后端驗證也很方便。
2)前端驗證代碼除非特殊情況或以學習練習為目的,就不要再自己一個個寫了,因為實際開發的時候是根本沒時間讓你慢慢自個去寫的。
3)真正工作時使用一般使用: jquery.validate.js 插件才能體會到前端驗證的酸爽,通過插件可以使用一些自帶的驗證方式,也可以自定義驗證規則
4.如果還不會使用jquery.validate.js可以去看我博客的另一篇文章《jquery.validate.js的使用教程》,一定會讓你受益匪淺的
---------------------

后端校驗比前端校驗更安全,更可靠,前端校驗可以增加用戶體驗,一般來說,在前端校驗的東西在后端也必須校驗(比如登陸用戶名、密碼),有些東西在前端就可以校驗,比如:字符串長度、郵箱格式、手機號碼等等,沒必要提交到后端,增加服務器的壓力,正常情況下,前端校驗的東西,最好后端都在校驗一次。

      放到后端校驗的,常見的與數據庫有關,比如輸入重復之類的,需要先查詢數據庫才知道,當然還有其他的一些東西。

 

//java檢測是否為電話號碼(手機、固定電話驗證)

String legalPhone = "";
String regExp ="^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}|[0]{1}[0-9]{2,3}-[0-9]{7,8}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(importPotentialBFOs[i].getLegalPhone());
if(m.find()){ //注意:m.find只能用一次,第二次調用后都為false
 legalPhone = importPotentialBFOs[i].getLegalPhone();
 uploadTmp.setLegalTelephone(legalPhone);

}else{
 throw new BizException("聯系電話格式錯誤!");
}
---------------------

參數驗證是一個常見的問題,無論是前端還是后台,都需對用戶輸入進行驗證,以此來保證系統數據的正確性。對於web來說,有些人可能理所當然的想在前端驗證就行了,但這樣是非常錯誤的做法,前端代碼對於用戶來說是透明的,稍微有點技術的人就可以繞過這個驗證,直接提交數據到后台。無論是前端網頁提交的接口,還是提供給外部的接口,參數驗證隨處可見,也是必不可少的。總之,一切用戶的輸入都是不可信的。

  參數驗證有許多種方式進行,下面以mvc為例,列舉幾種常見的驗證方式,假設有一個用戶注冊方法

  [HttpPost]
  public ActionResult Register(RegisterInfo info)

一、通過 if-if 判斷  

1
2
3
4
5
6
7
8
if ( string .IsNullOrEmpty(info.UserName))
{
     return  FailJson( "用戶名不能為空" );
}
if ( string .IsNullOrEmpty(info.Password))
{
     return  FailJson( "用戶密碼不能為空" )
}

  逐個對參數進行驗證,這種方式最粗暴,但當時在WebForm下也確實這么用過。對於參數少的方法還好,如果參數一多,就要寫n多的if-if,相當繁瑣,更重要的是這部分判斷沒法重用,另一個方法又是這樣判斷。

二、通過 DataAnnotation

  mvc提供了DataAnnotation對Action的Model進行驗證,說到底DataAnnotation就是一系列繼承了ValidationAttribute的特性,例如RangeAttribute,RequiredAttribute等等。ValidationAttribute 的虛方法IsValid 就是用來判斷被標記的對象是否符合當前規則。asp.net mvc在進行model binding的時候,會通過反射,獲取標記的ValidationAttribute,然后調用 IsValid 來判斷當前參數是否符合規則,如果驗證不通過,還會收集錯誤信息,這也是為什么我們可以在Action里通過ModelState.IsValid判斷Model驗證是否通過,通過ModelState來獲取驗證失敗信息的原因。例如上面的例子:

1
2
3
4
5
6
7
8
public  class  RegisterInfo
{
     [Required(ErrorMessage= "用戶名不能為空" )]
     public  string  UserName{ get ; set ;}
 
     [Required(ErrorMessage= "密碼不能為空" )]
     public  string  Password {  get set ; }
}

  事實上在webform上也可以參照mvc的實現原理實現這個過程。這種方式的優點的實現起來非常優雅,而且靈活,如果有多個Action共用一個Model參數的話,只要在一個地方寫就夠了,關鍵是它讓我們的代碼看起來非常簡潔。

  不過這種方式也有缺點,通常我們的項目可能會有很多的接口,比如幾十個接口,有些接口只有兩三個參數,為每個接口定義一個類包裝參數有點奢侈,而且實際上為這個類命名也是非常頭疼的一件事。

三、DataAnnotation 也可以標記在參數上

  通過驗證特性的AttributeUsage可以看到,它不只可以標記在屬性和字段上,也可以標記在參數上。也就是說,我們也可以這樣寫:

1
public  ActionResult Register([Required(ErrorMessage= "用戶名不能為空" )] string  userName, [Required(ErrorMessage= "密碼不能為空" )] string  password)

  這樣寫也是ok的,不過很明顯,這樣寫很方法參數會難看,特別是在有多個參數,或者參數有多種驗證規則的時候。

四、自定義ValidateAttribute

  我們知道可以利用過濾器在mvc的Action執行前做一些處理,例如身份驗證,授權處理的。同理,這里也可以用來對參數進行驗證。FilterAttribute是一個常見的過濾器,它允許我們在Action執行前后做一些操作,這里我們要做的就是在Action前驗證參數,如果驗證不通過,就不再執行下去了。

  定義一個BaseValidateAttribute基類如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  class  BaseValidateAttribute : FilterAttribute
{
     protected  virtual  void  HandleError(ActionExecutingContext context)
     {
         for  ( int  i = ValidateHandlerProviders.Handlers.Count; i > 0; i--)
         {
             ValidateHandlerProviders.Handlers[i - 1].Handle(context);
             if  (context.Result !=  null )
             {
                 break ;
             }
         }
     }
}

  HandleError 用於在驗證失敗時處理結果,這里ValidateHandlerProviders提過IValidateHandler用於處理結果,它可以在外部進行注冊。IValidateHandler定義如下:

1
2
3
4
public  interface  IValidateHandler
{
     void  Handle(ActionExecutingContext context);
}

  ValidateHandlerProviders定義如下,它有一個默認的處理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public  class  ValidateHandlerProviders
{
     public  static  List<IValidateHandler> Handlers {  get private  set ; }
 
     static  ValidateHandlerProviders()
     {
         Handlers =  new  List<IValidateHandler>()
         {
             new  DefaultValidateHandler()
         };
     }
 
     public  static  void  Register(IValidateHandler handler)
     {
         Handlers.Add(handler);
     }
}  

  這樣做的目的是,由於我們可能有很多具體的ValidateAttribute,可以把這模塊獨立開來,而把最終的處理過程交給外部決定,例如我們在項目中可以定義一個處理器:

1
2
3
4
5
6
7
8
9
10
public  class  StanderValidateHandler : IValidateHandler
{
     public  void  Handle(ActionExecutingContext filterContext)
     {
         filterContext.Result =  new  StanderJsonResult()
         {
             Result = FastStatnderResult.Fail( "參數驗證失敗" , 555)
         };
     }
}

  然后再應用程序啟動時注冊:ValidateHandlerProviders.Handlers.Add(new StanderValidateHandler());

  ValidateNullttribute:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public  class  ValidateNullAttribute : BaseValidateAttribute, IActionFilter
{
     public  bool  ValidateEmpty {  get set ; }
 
     public  string  Parameter {  get set ; }
 
     public  ValidateNullAttribute( string  parameter,  bool  validateEmpty =  false )
     {
         ValidateEmpty = validateEmpty;
         Parameter = parameter;
     }
 
     public  void  OnActionExecuting(ActionExecutingContext filterContext)
     {
         string [] validates = Parameter.Split( ',' );
         foreach  ( var  in  validates)
         {
             string  value = filterContext.HttpContext.Request[p];
             if (ValidateEmpty)
             {
                 if  ( string .IsNullOrEmpty(value))
                 {
                     base .HandleError(filterContext);
                 }
             }
             else
             {
                 if  (value ==  null )
                 {
                     base .HandleError(filterContext);
                 }
             }
         }
     }
 
     public  void  OnActionExecuted(ActionExecutedContext filterContext)
     {
 
     }
}

  ValidateRegexAttribute:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public  class  ValidateRegexAttribute : BaseValidateAttribute, IActionFilter
{
     private  Regex _regex;
 
     public  string  Pattern {  get set ; }
 
     public  string  Parameter {  get set ; }
 
     public  ValidateRegexAttribute( string  parameter,  string  pattern)
     {
         _regex =  new  Regex(pattern);
         Parameter = parameter;
     }
 
     public  void  OnActionExecuting(ActionExecutingContext filterContext)
     {
         string [] validates = Parameter.Split( ',' );
         foreach  ( var  in  validates)
         {
             string  value = filterContext.HttpContext.Request[p];
             if  (!_regex.IsMatch(value))
             {
                 base .HandleError(filterContext);
             }
         }
     }
 
     public  void  OnActionExecuted(ActionExecutedContext filterContext)
     {
 
     }
}

  更多的驗證同理實現即可。

  這樣,我們上面的寫法就變成:

1
2
[ValidateNull( "userName,password" )]
public  ActionResult Register( string  userName,  string  password)

  綜合看起來,還是ok的,與上面的DataAnnotation可以權衡選擇使用,這里我們可以擴展更多有用的信息,如錯誤描述等等。

總結

  當然每種方式都有有缺點,這個是視具體情況選擇了。一般參數太多建議就用一個對象包裝了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM