秦皇島做網(wǎng)站seo的快排seo軟件
一、故事背景
關于參數(shù)合法性驗證的重要性就不多說了,即使前端對參數(shù)做了基本驗證,后端依然也需要進行驗證,以防不合規(guī)的數(shù)據(jù)直接進入服務器,如果不對其進行攔截,嚴重的甚至會造成系統(tǒng)直接崩潰!
本文結(jié)合自己在項目中的實際使用經(jīng)驗,主要以實用為主,對數(shù)據(jù)合法性驗證做一次總結(jié),不了解的朋友可以學習一下,同時可以立馬實踐到項目上去。
下面我們通過幾個示例來演示如何判斷參數(shù)是否合法,廢話不多說,直接擼起來!
二、斷言驗證
對于參數(shù)的合法性驗證,最初的做法比較簡單,自定義一個異常類。
public class CommonException extends RuntimeException {private Integer code;public Integer getCode() {return code;}public CommonException(String message) {super(message);this.code = 500;}public CommonException(Integer code, String message) {super(message);this.code = code;}
}
當檢查到某個參數(shù)不合法的時候,直接拋異常!
@RestController
public class HelloController {@RequestMapping("/upload")public void upload(MultipartFile file) {if (file == null) {throw new CommonException("請選擇上傳文件!");}//.....}
}
最后寫一個統(tǒng)一異常攔截器,對拋異常的邏輯進行兜底處理。
這種做法比較簡單直觀,如果當前參數(shù)既要判斷是否為空,又要判斷長度是否超過最大限制的時候,代碼就會顯得很臃腫,而且復用性很差!
于是,程序界的大佬想到了一個更加優(yōu)雅又能節(jié)省代碼的方式,創(chuàng)建一個斷言類工具類,專門用來判斷參數(shù)的是否合法,如果不合法就拋異常,示例如下:
/*** 斷言工具類*/
public abstract class LocalAssert {public static void isTrue(boolean expression, String message) throws CommonException {if (!expression) {throw new CommonException(message);}}public static void isStringEmpty(String param, String message) throws CommonException{if(StringUtils.isEmpty(param)) {throw new CommonException(message);}}public static void isObjectEmpty(Object object, String message) throws CommonException {if (object == null) {throw new CommonException(message);}}public static void isCollectionEmpty(Collection coll, String message) throws CommonException {if (coll == null || (coll.size() == 0)) {throw new CommonException(message);}}
}
當我們需要對參數(shù)進行驗證的時候,直接通過這個類就可以完成,示例如下:
@RestController
public class HelloController {@RequestMapping("/save")public void save(String name, String email) {LocalAssert.isStringEmpty(name, "用戶名不能為空!");LocalAssert.isStringEmpty(email, "郵箱不能為空!");//.....}
}
相比上面的實現(xiàn)方式,這種處理邏輯,代碼明顯要簡潔的多!
類似這樣的工具類還很多,比如spring
也提供了一個名為Assert
的斷言工具類,在開發(fā)的時候,可以直接使用!
三、注解驗證
下面我們要介紹的是另一種更簡潔的參數(shù)驗證邏輯,使用注解來對數(shù)據(jù)進行合法性驗證,不僅代碼會變得很簡潔,閱讀起來也十分令人賞心悅目!
以 Spring Boot 工程為例,下面我們一起來看看具體的實踐方式。
3.1、添加依賴包
首先在pom.xml
中引入spring-boot-starter-web
依賴包即可,它會自動將注解驗證相關的依賴包打入工程!
<!-- spring boot web -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.2、編寫注解校驗請求對象
接著創(chuàng)建一個實體User
,用于封裝用戶注冊時的請求參數(shù),并在參數(shù)屬性上添加對應的注解驗證規(guī)則!
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;public class User {@NotBlank(message = "用戶名不能為空!")private String userName;@Email(message = "郵箱格式不正確")@NotBlank(message = "郵箱不能為空!")private String email;@NotBlank(message = "密碼不能為空!")@Size(min = 8, max = 16,message = "請輸入長度在8~16位的密碼")private String userPwd;@NotBlank(message = "確認密碼不能為空!")private String confirmPwd;// set、get方法等...
}
3.3、編寫請求接口
在web
層創(chuàng)建一個register()
注冊接口方法,同時在請求參數(shù)上添加@Valid
注解,示例如下:
import javax.validation.Valid;@RestController
public class UserController {@RequestMapping("/register")public ResultMsg register(@RequestBody @Valid User user){if(!user.getUserPwd().equals(user.getConfirmPwd())){throw new CommonException(4001, "確認密碼與密碼不相同,請確認!");}//業(yè)務處理...return ResultMsg.success();}
}
3.4、編寫全局異常處理器
最后自定義一個異常全局處理器,用于處理異常邏輯,如下:
@ControllerAdvice
public class GlobalExceptionHandler {private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 攔截Controller層的異常* @param e* @return*/@ExceptionHandler(value = {Exception.class})@ResponseBodypublic Object exceptionHandler(HttpServletRequest request, Exception e){LOGGER.error("【統(tǒng)一異常攔截】請求地址:{}, 錯誤信息:{}", request.getRequestURI(), e.getMessage());// 注解驗證拋出的異常if(e instanceof MethodArgumentNotValidException){// 獲取錯誤信息String error = ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage();return ResultMsg.fail(500, error);}// 自定義拋出的異常if(e instanceof CommonException){return ResultMsg.fail(((CommonException) e).getCode(), e.getMessage());}return ResultMsg.fail(999, e.getMessage());}
}
統(tǒng)一響應對象ResultMsg
,如下:
public class ResultMsg<T> {/**狀態(tài)碼**/private int code;/**結(jié)果描述**/private String message;/**結(jié)果集**/private T data;/**時間戳**/private long timestamp;// set、get方法等...
}
3.5、服務測試
啟動項目,使用postman
來驗證一下代碼的正確性,看看效果如何?
- 測試字段是否為空
- 測試郵箱是否合法
- 測試密碼長度是否符合要求
- 測試密碼與確認密碼是否相同
可以看到,驗證結(jié)果與預期一致!
四、自定義注解驗證
事實上,熟悉 SpringMVC 源碼的同學可能知道,Spring Boot 內(nèi)置了一個hibernate-validator
校驗組件,上文就是利用它來完成對請求時入?yún)⑸系淖⒔怛炞C。
默認的情況下,依賴包已經(jīng)給我們提供了非常多的校驗注解,如下!
- JSR 提供的校驗注解!
- Hibernate Validator 提供的校驗注解
但是某些情況,例如性別這個參數(shù),可能需要我們自己去手動驗證。
針對這種情況,我們也可以自定義一個注解來完成參數(shù)的校驗,也便于進一步了解注解驗證的原理。
自定義注解驗證,實現(xiàn)方式如下!
首先,創(chuàng)建一個Sex
注解。
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = SexValidator.class)
@Documented
public @interface Sex {String message() default "性別值不在可選范圍內(nèi)";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
然后,創(chuàng)建一個SexValidator
類,實現(xiàn)自ConstraintValidator
接口
public class SexValidator implements ConstraintValidator<Sex, String> {@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {Set<String> sexSet = new HashSet<String>();sexSet.add("男");sexSet.add("女");return sexSet.contains(value);}
}
最后,在User
實體類上加入一個性別參數(shù),使用自定義注解進行校驗!
public class User {@NotBlank(message = "用戶名不能為空!")private String userName;@Email(message = "郵箱格式不正確")@NotBlank(message = "郵箱不能為空!")private String email;@NotBlank(message = "密碼不能為空!")@Size(min = 8, max = 16,message = "請輸入長度在8~16位的密碼")private String userPwd;/*** 自定義注解校驗*/@Sex(message = "性別輸入有誤!")private String sex;// set、get方法等...
}
啟動服務,重新請求,運行結(jié)果如下:
結(jié)果與預期一致!
五、總結(jié)
參數(shù)驗證,在開發(fā)中使用非常頻繁,如何優(yōu)雅的進行驗證,讓代碼變得更加可讀,是業(yè)界大佬一直在追求的目標!
本文主要圍繞在 Spring Boot 中實現(xiàn)參數(shù)統(tǒng)一驗證進行相關的知識總結(jié)和介紹,如果有描述不對的地方,歡迎留言支持。
示例代碼:spring-boot-example-valid