Springboot接口參數(shù)校驗的方法
在設計接口時我們通常需要對接口中的非法參數(shù)做校驗,以降低在程序運行時因為一些非法參數(shù)而導致程序發(fā)生異常的風險,例如登錄的時候需要校驗用戶名密碼是否為空,創(chuàng)建用戶的時候需要校驗郵件、手機號碼格式是否準確。如果在代碼中對接口參數(shù)一個個硬編碼校驗的話就太繁瑣了,代碼可讀性極差。這時不妨試試Validator框架。
原生的依賴是validation-api而hibernate-validator是對validation-api的增強,hibernate-validator框架已經(jīng)集成在spring-boot-starter-web中。
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.0.Final</version> </dependency>
但從springboot-2.3開始,校驗包被獨立成了一個starter組件,所以需要引入validation和web,而springboot-2.3之前的版本只需要引入web依賴就可以了。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
部分常見注解如下:
注解 | 功能 |
@AssertFalse | 可以為null,如果不為null的話必須為false |
@AssertTrue | 可以為null,如果不為null的話必須為true |
@DecimalMax | 設置不能超過最大值 |
@DecimalMin | 設置不能超過最小值 |
@Digits | 設置必須是數(shù)字且數(shù)字整數(shù)的位數(shù)和小數(shù)的位數(shù)必須在指定范圍內(nèi) |
@Future | 日期必須在當前日期的未來 |
@Past | 日期必須在當前日期的過去 |
@Max | 最大不得超過此最大值 |
@Min | 最大不得小于此最小值 |
@NotNull | 不能為null,可以是空 |
@Null | 必須為null |
@Pattern | 必須滿足指定的正則表達式 |
@Size | 集合、數(shù)組、map等的size()值必須在指定范圍內(nèi) |
必須是email格式 | |
@Length | 長度必須在指定范圍內(nèi) |
@NotBlank | 字符串不能為null,字符串trim()后也不能等于“” |
@NotEmpty | 不能為null,集合、數(shù)組、map等size()不能為0;字符串trim()后可以等于“” |
@Range | 值必須在指定范圍內(nèi) |
@URL | 必須是一個URL |
一、基礎注解使用
注解簡單演示類
import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; @Data public class UserVo { @Length(min = 4, max = 12, message = "賬號長度必須位于4到10之間") private String userCode; @NotBlank(message = "用戶名不能為空") private String userName; @Email(message = "郵箱格式有誤") private String email; @Pattern(regexp = "^1[3456789]\\d{9}$", message = "手機號格式錯誤") private String phone; /** * 0:普通用戶;VIP:會員用戶;2:超級VIP用戶 */ @Pattern(regexp = "[012]", message = "用戶類型有誤,請輸入0:普通用戶;VIP:會員用戶;2:超級VIP用戶") private String type; }
在全局異常處理里面加上對參數(shù)校驗異常的攔截
@ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class}) public ResponseVo validatedExceptionHandler(Exception e) { log.error("參數(shù)校驗異常!原因是{}", e); if (e instanceof MethodArgumentNotValidException) { //BeanValidation exception MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e; return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(), ex.getBindingResult().getAllErrors().stream() .map(ObjectError::getDefaultMessage) .collect(Collectors.joining("; "))); } else if (e instanceof ConstraintViolationException) { //BeanValidation GET simple param ConstraintViolationException ex = (ConstraintViolationException) e; return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(), ex.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .collect(Collectors.joining("; "))); } else if (e instanceof BindException) { //BeanValidation GET object param BindException ex = (BindException) e; return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(), ex.getAllErrors().stream() .map(ObjectError::getDefaultMessage) .collect(Collectors.joining("; "))); } return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(), ResponseEnum.BODY_NOT_MATCH.getResultMsg()); }
定義參數(shù)校驗的controller
import com.yx.light.element.mybatis.vo.UserVo; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.constraints.Email; @RestController @RequestMapping(value = "/valid") @Validated @Slf4j public class ValidController { /** * 單參數(shù)校驗,單參數(shù)需要在controller上加上@Validated配合使用 * @param email * @return */ @PostMapping(value = "/oneParam", produces = "application/json; charset=UTF-8") public String oneParam(@Email(message = "郵箱格式有誤") String email) { log.info("校驗郵箱為:{}", email); return "郵箱校驗成功"; } /** * body類型校驗 * @param vo * @return */ @PostMapping(value = "/bodyParam", produces = "application/json; charset=UTF-8") public String bodyParam(@Validated @RequestBody UserVo vo) { log.info("校驗body為:{}", vo); return "body校驗成功"; } /** * 表單參數(shù)校驗 * @param vo * @return */ @PostMapping(value = "/formParam", produces = "application/json; charset=UTF-8") public String formParam(@Validated UserVo vo) { log.info("校驗表單為:{}", vo); return "表單校驗成功"; } }
oneParam接口
bodyParam接口
formParam接口
二、自定義參數(shù)校驗
當然你也可以自定義參數(shù)校驗,出現(xiàn)有些復雜的邏輯基本注解是沒辦法兼容的,需要我們自己去實現(xiàn)自動校驗。
自定義校驗注解
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Documented @Retention(RUNTIME) @Target({FIELD, METHOD, PARAMETER, TYPE}) @Constraint(validatedBy = UserValidation.UniqueUserValidator.class) public @interface UniqueUser { String message() default "用戶編碼、手機號碼、郵箱不允許與現(xiàn)存用戶重復"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
想讓自定義驗證注解生效,需要實現(xiàn)ConstraintValidator接口。接口的第一個參數(shù)是 自定義注解類型,第二個參數(shù)是 被注解字段的類。
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.lang.annotation.Annotation; import java.util.function.Predicate; @Slf4j public class UserValidation<T extends Annotation> implements ConstraintValidator<T, UserVo> { protected Predicate<UserVo> predicate = c -> true; @Override public boolean isValid(UserVo user, ConstraintValidatorContext constraintValidatorContext) { return predicate.test(user); } /** * 校驗用戶是否唯一 * 即判斷數(shù)據(jù)庫是否存在當前新用戶的信息,如用戶編碼,手機,郵箱 */ public static class UniqueUserValidator extends UserValidation<UniqueUser> { @Override public void initialize(UniqueUser uniqueUser) { predicate = c -> !existsUser(c.getUserCode(), c.getEmail(), c.getPhone()); } private boolean existsUser(String userCode, String email, String phone) { //TODO 寫一個數(shù)據(jù)庫查詢判斷是否存在相同的用戶,省略... return true; } } }
在controller中添加測試接口
@PostMapping(value = "/addUser", produces = "application/json; charset=UTF-8") public String addUser(@UniqueUser @Validated @RequestBody UserVo vo) { log.info("校驗body為:{}", vo); return "新增成功"; }
addUser接口
三、分組校驗
如果你的一個實體中的字段某一些是新增的時候必傳,某一些修改時又不用傳,那么對于不用傳的字段肯定不需要校驗的,這時候如果我們共用一個實體作為多個接口參數(shù)那肯定存在兼容問題,此時你就可以考慮將參數(shù)分組判斷。
定義一個分組接口ValidGroup讓其繼承javax.validation.groups.Default,再在分組接口中定義出多個不同的操作類型,Create,Update,Query,Delete。
import javax.validation.groups.Default; public interface ValidGroup extends Default { interface Crud extends ValidGroup { interface Create extends Crud { } interface Update extends Crud { } interface Query extends Crud { } interface Delete extends Crud { } } }
稍微修改一下原來的vo,給他們加上分組參數(shù)groups
import com.yx.light.element.mybatis.validation.ValidGroup; import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; @Data public class UserVo { @Length(min = 4, max = 12, message = "賬號長度必須位于4到10之間", groups = ValidGroup.Crud.Update.class) private String userCode; @NotBlank(message = "用戶名不能為空", groups = ValidGroup.Crud.Create.class) private String userName; @Email(message = "郵箱格式有誤") private String email; @Pattern(regexp = "^1[3456789]\\d{9}$", message = "手機號格式錯誤") private String phone; /** * 0:普通用戶;VIP:會員用戶;2:超級VIP用戶 */ @Pattern(regexp = "[012]", message = "用戶類型有誤,請輸入0:普通用戶;VIP:會員用戶;2:超級VIP用戶") private String type; }
在controller中添加如下方法,這里我們通過value屬性給addUserV2()和updateUser()方法分別指定Create和Update分組。
@PostMapping(value = "/addUserV2", produces = "application/json; charset=UTF-8") public String addUserV2(@Validated(value = ValidGroup.Crud.Create.class) @RequestBody UserVo vo) { log.info("校驗body為:{}", vo); return "新增成功"; } @PostMapping(value = "/updateUser", produces = "application/json; charset=UTF-8") public String updateUser(@Validated(value = ValidGroup.Crud.Update.class) @RequestBody UserVo vo) { log.info("校驗body為:{}", vo); return "更新成功"; }
addUserV2接口
updateUser接口
到此這篇關于Springboot接口參數(shù)校驗的文章就介紹到這了,更多相關Springboot參數(shù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
RestFul風格 — 使用@PathVariable傳遞參數(shù)報錯404的解決
這篇文章主要介紹了RestFul風格 — 使用@PathVariable傳遞參數(shù)報錯404的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10Java數(shù)據(jù)結(jié)構(gòu)之順序表的實現(xiàn)
線性表(linear?list)是n個具有相同特性的數(shù)據(jù)元素的有限序列。順序表是常見的線性表之一,本文將詳細講講順序表的原理與實現(xiàn),需要的可以參考一下2022-08-08

在Spring Boot中使用swagger-bootstrap-ui的方法

Java將CSV的數(shù)據(jù)發(fā)送到kafka的示例