Springboot接口參數(shù)校驗(yàn)的方法
在設(shè)計(jì)接口時(shí)我們通常需要對(duì)接口中的非法參數(shù)做校驗(yàn),以降低在程序運(yùn)行時(shí)因?yàn)橐恍┓欠▍?shù)而導(dǎo)致程序發(fā)生異常的風(fēng)險(xiǎn),例如登錄的時(shí)候需要校驗(yàn)用戶名密碼是否為空,創(chuàng)建用戶的時(shí)候需要校驗(yàn)郵件、手機(jī)號(hào)碼格式是否準(zhǔn)確。如果在代碼中對(duì)接口參數(shù)一個(gè)個(gè)硬編碼校驗(yàn)的話就太繁瑣了,代碼可讀性極差。這時(shí)不妨試試Validator框架。
原生的依賴是validation-api而hibernate-validator是對(duì)validation-api的增強(qiáng),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開始,校驗(yàn)包被獨(dú)立成了一個(gè)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 | 設(shè)置不能超過最大值 |
| @DecimalMin | 設(shè)置不能超過最小值 |
| @Digits | 設(shè)置必須是數(shù)字且數(shù)字整數(shù)的位數(shù)和小數(shù)的位數(shù)必須在指定范圍內(nèi) |
| @Future | 日期必須在當(dāng)前日期的未來 |
| @Past | 日期必須在當(dāng)前日期的過去 |
| @Max | 最大不得超過此最大值 |
| @Min | 最大不得小于此最小值 |
| @NotNull | 不能為null,可以是空 |
| @Null | 必須為null |
| @Pattern | 必須滿足指定的正則表達(dá)式 |
| @Size | 集合、數(shù)組、map等的size()值必須在指定范圍內(nèi) |
| 必須是email格式 | |
| @Length | 長(zhǎng)度必須在指定范圍內(nèi) |
| @NotBlank | 字符串不能為null,字符串trim()后也不能等于“” |
| @NotEmpty | 不能為null,集合、數(shù)組、map等size()不能為0;字符串trim()后可以等于“” |
| @Range | 值必須在指定范圍內(nèi) |
| @URL | 必須是一個(gè)URL |
一、基礎(chǔ)注解使用
注解簡(jiǎn)單演示類
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 = "賬號(hào)長(zhǎng)度必須位于4到10之間")
private String userCode;
@NotBlank(message = "用戶名不能為空")
private String userName;
@Email(message = "郵箱格式有誤")
private String email;
@Pattern(regexp = "^1[3456789]\\d{9}$", message = "手機(jī)號(hào)格式錯(cuò)誤")
private String phone;
/**
* 0:普通用戶;VIP:會(huì)員用戶;2:超級(jí)VIP用戶
*/
@Pattern(regexp = "[012]", message = "用戶類型有誤,請(qǐng)輸入0:普通用戶;VIP:會(huì)員用戶;2:超級(jí)VIP用戶")
private String type;
}在全局異常處理里面加上對(duì)參數(shù)校驗(yàn)異常的攔截
@ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
public ResponseVo validatedExceptionHandler(Exception e) {
log.error("參數(shù)校驗(yàn)異常!原因是{}", 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ù)校驗(yàn)的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ù)校驗(yàn),單參數(shù)需要在controller上加上@Validated配合使用
* @param email
* @return
*/
@PostMapping(value = "/oneParam", produces = "application/json; charset=UTF-8")
public String oneParam(@Email(message = "郵箱格式有誤") String email) {
log.info("校驗(yàn)郵箱為:{}", email);
return "郵箱校驗(yàn)成功";
}
/**
* body類型校驗(yàn)
* @param vo
* @return
*/
@PostMapping(value = "/bodyParam", produces = "application/json; charset=UTF-8")
public String bodyParam(@Validated @RequestBody UserVo vo) {
log.info("校驗(yàn)body為:{}", vo);
return "body校驗(yàn)成功";
}
/**
* 表單參數(shù)校驗(yàn)
* @param vo
* @return
*/
@PostMapping(value = "/formParam", produces = "application/json; charset=UTF-8")
public String formParam(@Validated UserVo vo) {
log.info("校驗(yàn)表單為:{}", vo);
return "表單校驗(yàn)成功";
}
}oneParam接口

bodyParam接口

formParam接口

二、自定義參數(shù)校驗(yàn)
當(dāng)然你也可以自定義參數(shù)校驗(yàn),出現(xiàn)有些復(fù)雜的邏輯基本注解是沒辦法兼容的,需要我們自己去實(shí)現(xiàn)自動(dòng)校驗(yàn)。
自定義校驗(yà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 "用戶編碼、手機(jī)號(hào)碼、郵箱不允許與現(xiàn)存用戶重復(fù)";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}想讓自定義驗(yàn)證注解生效,需要實(shí)現(xiàn)ConstraintValidator接口。接口的第一個(gè)參數(shù)是 自定義注解類型,第二個(gè)參數(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);
}
/**
* 校驗(yàn)用戶是否唯一
* 即判斷數(shù)據(jù)庫是否存在當(dāng)前新用戶的信息,如用戶編碼,手機(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 寫一個(gè)數(shù)據(jù)庫查詢判斷是否存在相同的用戶,省略...
return true;
}
}
}在controller中添加測(cè)試接口
@PostMapping(value = "/addUser", produces = "application/json; charset=UTF-8")
public String addUser(@UniqueUser @Validated @RequestBody UserVo vo) {
log.info("校驗(yàn)body為:{}", vo);
return "新增成功";
}addUser接口

三、分組校驗(yàn)
如果你的一個(gè)實(shí)體中的字段某一些是新增的時(shí)候必傳,某一些修改時(shí)又不用傳,那么對(duì)于不用傳的字段肯定不需要校驗(yàn)的,這時(shí)候如果我們共用一個(gè)實(shí)體作為多個(gè)接口參數(shù)那肯定存在兼容問題,此時(shí)你就可以考慮將參數(shù)分組判斷。
定義一個(gè)分組接口ValidGroup讓其繼承javax.validation.groups.Default,再在分組接口中定義出多個(gè)不同的操作類型,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 = "賬號(hào)長(zhǎng)度必須位于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 = "手機(jī)號(hào)格式錯(cuò)誤")
private String phone;
/**
* 0:普通用戶;VIP:會(huì)員用戶;2:超級(jí)VIP用戶
*/
@Pattern(regexp = "[012]", message = "用戶類型有誤,請(qǐng)輸入0:普通用戶;VIP:會(huì)員用戶;2:超級(jí)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("校驗(yàn)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("校驗(yàn)body為:{}", vo);
return "更新成功";
}addUserV2接口

updateUser接口

到此這篇關(guān)于Springboot接口參數(shù)校驗(yàn)的文章就介紹到這了,更多相關(guān)Springboot參數(shù)校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
RestFul風(fēng)格 — 使用@PathVariable傳遞參數(shù)報(bào)錯(cuò)404的解決
這篇文章主要介紹了RestFul風(fēng)格 — 使用@PathVariable傳遞參數(shù)報(bào)錯(cuò)404的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Java數(shù)據(jù)結(jié)構(gòu)之順序表的實(shí)現(xiàn)
線性表(linear?list)是n個(gè)具有相同特性的數(shù)據(jù)元素的有限序列。順序表是常見的線性表之一,本文將詳細(xì)講講順序表的原理與實(shí)現(xiàn),需要的可以參考一下2022-08-08
在Spring Boot中使用swagger-bootstrap-ui的方法
Java將CSV的數(shù)據(jù)發(fā)送到kafka的示例

