SpringMVC參數校驗




SpringMVC是根據參數的名字,然后用setter方法來對數據進行綁定的,若類型沒有匹配上則會出現400的錯誤,同時還要注意空值問題


1. 參數校驗

我們在做Web層的時候,接收了各種參數,盡管前端已經做了驗證,但難免惡意傳參,所以要對傳過來的數據保持不信任的態度來進行參數校驗


筆者日常進行驗證的方式如下:

@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(String name, String email) {
	 
	if(name == null || name.isEmpty()){
		return "名字不能為空";
	}
	if(email == null || email.isEmpty()){
        
        // 這里還要加上郵箱格式的驗證,省略省略
        
		return "郵箱不能為空";
	}  
}

乍一看好像沒什么問題,能夠應付需求,但是一旦參數多了起來就會像下面那樣


@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(String name, String email, String sex, String password, String nickName, String address) {
	 
	if(name == null || name.isEmpty()){
		return "名字不能為空";
	}
	if(email == null || email.isEmpty()){
		return "郵箱不能為空";
	}  
    if(sex == null || sex.isEmpty()){
		return "性別不能為空";
	}
	if(password == null || password.isEmpty()){
		return "密碼不能為空";
	}
    if(address == null || address.isEmpty()){
		return "地址不能為空";
	}  
}

這里看還挺整齊的,一目了然,其實除了非空判斷還需各種格式驗證沒有列出了,如果再添加參數就成了累贅,一個類中參數校驗的代碼就占了大部分,得不償失


這時候就該考慮簡便的參數校驗方式了——JSR-303(基於注解)





2. JSR-303

JSR-303是一個被提出來的數據驗證規范,所以這僅僅是個接口,沒有具體實現的功能,容易被誤解為JSR-303就是用於數據驗證的的工具。我們要用到JSR-303的規范,那么就需要導入實現類的jar包,比如Hibernate Validator也是我們后面使用的jar包。


Spring也提供了參數校驗的方式,即實現其內部的validator接口來進行參數校驗,接口有兩個方法:

public class UserValidator implements Validator {

    // 判斷是否支持驗證該類
    public boolean supports(Class clazz) {
        return User.class.equals(clazz);
    }

    // 校驗數據,將報錯信息放入Error對象中
    public void validate(Object obj, Errors e) {
        // ValidationUtils的靜態方法rejectIfEmpty(),對屬性進行非空判斷
        ValidationUtils.rejectIfEmpty(e,"name","name.empty");
        User user = (User)obj;
        if(user.getAge() < 0){
            e.rejectValue("age", "年齡不能為負數");
        }
    }
}

我們當然不滿足那么麻煩的方法,所以JSR-303出場


JSR-303是基於注解校驗的,注解已經實現了各種限制,我們可以將注解標記在需要校驗的類的屬性上,或是對應的setter方法上(筆者習慣標記在屬性上)


導入Hibernate Validator依賴jar包,筆者使用maven工程

<!--    參數校驗    -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.2.Final</version>
</dependency>

hibernate-validator實現了JSR-303的所有功能,額外還提供了一些實用的注解。我們可以將其分成兩部分,一個是JSR-303規范中包含的,另一部分是hibernate額外提供的。下面的注解看解釋就能明白是什么功能了


JSR-303規范

Annotation Description
@Null 被注釋的元素必須為 null
@NotNull 被注釋的元素必須不為 null
@AssertTrue 被注釋的元素必須為 true
@AssertFalse 被注釋的元素必須為 false
@Min(value) 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value) 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value) 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value) 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min) 被注釋的元素的大小必須在指定的范圍內
@Digits (integer, fraction) 被注釋的元素必須是一個數字,其值必須在可接受的范圍內
@Past 被注釋的元素必須是一個過去的日期
@Future 被注釋的元素必須是一個將來的日期
@Pattern(value) 被注釋的元素必須符合指定的正則表達式

hibernate額外提供的

Constraint 詳細信息
@Email 被注釋的元素必須是電子郵箱地址
@Length 被注釋的字符串的大小必須在指定的范圍內
@NotEmpty 被注釋的字符串的必須非空
@Range 被注釋的元素必須在合適的范圍內





3. JSR-303的簡單使用


3.1 在需要校驗的屬性上標記注解

注解有個屬性message存放自定義的錯誤信息


public class User {

    @NotNull(message = "名字不能為空")
    private String name;

    @Email(message = "郵箱格式錯誤")
    private String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    // 各種getter / setter / 構造器
}

3.2 開啟校驗

在Controller方法入參中需要校驗的參數前加入@Validated()表明需要校驗,后方要加@BindingResult接收錯誤信息,若沒加即接收不了錯誤信息會報錯(若使用了全局異常處理則可以不加)。@Validated()和@BindingResult二者一前一后緊密相連的,中間不能有任何數值相隔。


@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(@Validated() User user, BindingResult bindingResult) {

    // 判斷是否有錯
    if (bindingResult.hasErrors()) {
        // 獲取字段上的錯誤
        FieldError errors = bindingResult.getFieldError();
        // 輸出message信息
        return (errors.getDefaultMessage() + "\n");
    }
    // dosomething
}

3.3 補充

按上面的方法日常使用應該沒什么問題了,數據校驗中還有分組自定義校驗的知識點,這里筆者就不做 (tou) 說明 (lan) 了





4. 筆者遇到的小插曲

我們知道前端傳參過來都是字符串,經過Spring的類型轉換器轉換成為我們需要的類型才能正常使用,之前筆者沒有使用JSR-303規范來校驗參數的時候莫得發覺問題,但這也為現在埋下了坑


如果傳個整型呢?

public class User {

    @Min(value = 0, message = "不能為負數")
    private int id;
    
    // 各種getter / setter / 構造器
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String listByPage(@Validated() User user, BindingResult bindingResult) {

    if (bindingResult.hasErrors()) {
        FieldError errors = bindingResult.getFieldError();
        return (errors.getDefaultMessage() + "\n");
     }
    
    // dosomething
}

乍一看沒有什么問題,普通使用能過去。但是但是但是 int id 傳了空值就會報錯:

Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'id'; nested exception is java.lang.NumberFormatException: For input string: ""

// 翻譯:轉換String到int id失敗,報錯原因是數字格式化異常,因為輸入了字符串 “”

這里就是那個小小小的插曲,開始真是不知如何解決


解決方法

使用包裝類Integer,類型對不上就不匹配了,包裝類還會自動裝箱和拆箱,所以很方便解決空值問題

// Integer id

// 替換成包裝類之后傳的參數為,空值不接收即為null
User{id=null, name='jiafu liu', email='1210911104@qq.com'}

教訓是:對於可能會傳空值的屬性一般會用包裝類型




免責聲明!

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



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