SpringBoot2.x 參數(shù)校驗(yàn)問(wèn)題小結(jié)
本文主要對(duì)SpringBoot2.x參數(shù)校驗(yàn)進(jìn)行簡(jiǎn)單總結(jié),其中SpringBoot使用的2.4.5
版本。
一、引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- lombok插件 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency>
二、實(shí)體類
User類:
package com.rtxtitanv.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.*; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.model.User * @description 用戶實(shí)體類 * @date 2021/8/15 16:28 */ @AllArgsConstructor @NoArgsConstructor @Data public class User { @NotNull(message = "id不能為空") private Long id; @Length(min = 6, max = 20, message = "用戶名長(zhǎng)度不小于6,不超過(guò)20") @NotNull(message = "用戶名不能為空") private String username; @Pattern(regexp = "^[A-Z][A-Za-z0-9_]{5,19}$", message = "密碼以大寫英文字母開(kāi)頭,只包含英文字母、數(shù)字、下劃線,長(zhǎng)度在6到20之間") @NotNull(message = "密碼不能為空") private String password; @Max(value = 60, message = "年齡最大為60") @Min(value = 18, message = "年齡最小為18") @NotNull(message = "年齡不能為空") private Integer age; @Email(message = "郵箱格式不正確") @NotEmpty(message = "郵箱不能為空") private String email; private String rank; }
通用響應(yīng)類:
package com.rtxtitanv.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.http.HttpStatus; import java.io.Serializable; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.model.CommonResult * @description 通用響應(yīng)類 * @date 2021/8/15 17:35 */ @AllArgsConstructor @NoArgsConstructor @Data public class CommonResult<T> implements Serializable { private static final long serialVersionUID = 5231430760082814286L; private int code; private String message; private T data; public static <T> CommonResult<T> ok(String message, T data) { return new CommonResult<T>(HttpStatus.OK.value(), message, data); } public static <T> CommonResult<T> fail(int code, String message, T data) { return new CommonResult<>(code, message, data); } }
三、常用的校驗(yàn)注解
這里對(duì)一些用于參數(shù)校驗(yàn)的常用注解進(jìn)行總結(jié):
@Null
:必須為null。@NotNull
:必須不為null。@AssertTrue
:必須為true。@AssertFalse
:必須為false。@Min(value)
:必須是一個(gè)大于等于指定值的數(shù)字。@Max(value)
:必須是一個(gè)小于等于指定值的數(shù)字。@DecimalMin(value)
:必須是一個(gè)大于等于指定值的數(shù)字。@DecimalMax(value)
:必須是一個(gè)小于等于指定值的數(shù)字。@Size(min=,max=)
:大小必須在指定的范圍內(nèi)。@Digits(integer, fraction)
:必須是一個(gè)數(shù)字,其值必須在可接受的范圍內(nèi)。@Past
:必須是一個(gè)過(guò)去的日期。@Future
:必須是一個(gè)將來(lái)的日期。@Pattern(regex=)
:必須符合指定的正則表達(dá)式。@Email
:必須是一個(gè)有效的email地址。@Length(min=,max=)
:字符串長(zhǎng)度是否在指定范圍內(nèi)。@NotBlank
:必須非空且長(zhǎng)度大于0。@NotEmpty
:必須不為null或空。@URL(protocol=,host,port)
:必須是一個(gè)有效的URL,如果提供了protocol,host等,則還需滿足提供的條件。
四、校驗(yàn)Controller中的參數(shù)
1.校驗(yàn)請(qǐng)求體
創(chuàng)建UserController
,在需要校驗(yàn)的參數(shù)上添加@Valid
注解:
package com.rtxtitanv.controller; import com.rtxtitanv.model.CommonResult; import com.rtxtitanv.model.User; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.controller.UserController * @description UserController * @date 2021/8/15 16:33 */ @RequestMapping("/user") @RestController public class UserController { @PostMapping("/save") public CommonResult<User> saveUser(@RequestBody @Valid User user) { return CommonResult.ok("保存用戶成功", user); } }
啟動(dòng)項(xiàng)目,發(fā)送如下POST請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/save
:
如果驗(yàn)證失敗會(huì)拋出MethodArgumentNotValidException
,默認(rèn)情況下,Spring會(huì)將MethodArgumentNotValidException
異常轉(zhuǎn)換為HTTP Status 400。查看控制臺(tái)打印的日志發(fā)現(xiàn)拋出了MethodArgumentNotValidException
:
創(chuàng)建全局異常處理類捕獲異常并進(jìn)行處理:
package com.rtxtitanv.handler; import com.rtxtitanv.model.CommonResult; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap; import java.util.Map; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.handler.GlobalExceptionHandler * @description 全局異常處理類 * @date 2021/8/15 16:36 */ @RestControllerAdvice(annotations = {Controller.class, RestController.class}) public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(code = HttpStatus.BAD_REQUEST) public CommonResult<Map<String, String>> validateException(MethodArgumentNotValidException e) { Map<String, String> errors = new HashMap<>(16); e.getBindingResult().getAllErrors() .forEach(error -> errors.put(((FieldError)error).getField(), error.getDefaultMessage())); return CommonResult.fail(HttpStatus.BAD_REQUEST.value(), "無(wú)效的參數(shù)", errors); } }
重啟項(xiàng)目后再次發(fā)送如下POST請(qǐng)求:
2.校驗(yàn)請(qǐng)求參數(shù)
UserController
上添加Validated
注解并新增以下方法:
@GetMapping("/get/{id}") public CommonResult<User> getUserById(@Valid @PathVariable(value = "id") @Min(value = 1, message = "id不能小于1") Long id) { User user = new User(id, "ZhaoYun", "A123456sd", 20, "zhaoyun123@xxx.com", "黃金"); return CommonResult.ok("根據(jù)id查詢用戶成功", user); } @DeleteMapping("/delete") public CommonResult<User> deleteByUsername( @Valid @RequestParam(value = "username") @Size(min = 6, max = 20, message = "用戶名長(zhǎng)度不在指定范圍內(nèi)") String username) { User user = new User(1L, username, "A123456sd", 20, "zhaoyun123@xxx.com", "黃金"); return CommonResult.ok("根據(jù)用戶名刪除用戶成功", user); }
全局異常處理類中新增以下方法:
@ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR) public CommonResult<Map<String, String>> handleConstraintViolationException(ConstraintViolationException e) { Map<String, String> errors = new HashMap<>(16); e.getConstraintViolations().forEach(constraintViolation -> errors .put(constraintViolation.getPropertyPath().toString(), constraintViolation.getMessage())); return CommonResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "無(wú)效的參數(shù)", errors); }
發(fā)送如下GET請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/get/0
:
發(fā)送如下DELETE請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/delete?username=ZhaoZiLong_1896582826
:
五、校驗(yàn)Service中的參數(shù)
通過(guò)@Validated
和@Valid
注解組合使用不僅可以校驗(yàn)Controller中的參數(shù),還可以校驗(yàn)任何Spring組件中的參數(shù)。
UserService
:
package com.rtxtitanv.service; import com.rtxtitanv.model.CommonResult; import com.rtxtitanv.model.User; import org.springframework.validation.annotation.Validated; import javax.validation.Valid; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.service.UserService * @description UserService * @date 2021/8/15 16:46 */ @Validated public interface UserService { /** * 更新用戶 * * @param user 用戶參數(shù) * @return CommonResult<User> */ CommonResult<User> updateUser(@Valid User user); }
UserService
實(shí)現(xiàn)類:
package com.rtxtitanv.service.impl; import com.rtxtitanv.model.CommonResult; import com.rtxtitanv.model.User; import com.rtxtitanv.service.UserService; import org.springframework.stereotype.Service; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.service.impl.UserServiceImpl * @description UserService實(shí)現(xiàn)類 * @date 2021/8/15 16:46 */ @Service public class UserServiceImpl implements UserService { @Override public CommonResult<User> updateUser(User user) { return CommonResult.ok("更新用戶成功", user); } }
UserController
中新增以下代碼:
@Resource private UserService userService; @PutMapping("/update") public CommonResult<User> updateUser(@RequestBody User user) { return userService.updateUser(user); }
發(fā)送如下PUT請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/update
:
六、編程式校驗(yàn)
通過(guò)Validator
實(shí)例可以手動(dòng)進(jìn)行參數(shù)校驗(yàn)。UserController
中新增以下代碼:
@Resource private Validator validator; @PostMapping("/insert") public CommonResult<User> insertUser(@RequestBody User user) { if (!validator.validate(user).isEmpty()) { throw new ConstraintViolationException(validator.validate(user)); } return CommonResult.ok("添加用戶成功", user); }
發(fā)送如下POST請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/insert
:
七、自定義校驗(yàn)注解
如果自帶的校驗(yàn)注解無(wú)法滿足需求,還可以自定義校驗(yàn)注解。首先創(chuàng)建如下注解用于密碼校驗(yàn):
package com.rtxtitanv.annotation; import com.rtxtitanv.validator.PasswordValidator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.annotation.Password * @description 自定義密碼校驗(yàn)注解 * @date 2021/8/16 14:54 */ @Documented @Constraint(validatedBy = PasswordValidator.class) @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Password { String message() default "無(wú)效密碼"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
然后創(chuàng)建ConstraintValidator
接口的實(shí)現(xiàn)類并重寫isValid
方法:
package com.rtxtitanv.validator; import com.rtxtitanv.annotation.Password; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.validator.PasswordValidator * @description PasswordValidator * @date 2021/8/16 15:01 */ public class PasswordValidator implements ConstraintValidator<Password, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) { return true; } // 密碼必須以大寫英文字母開(kāi)頭,只包含英文字母、數(shù)字、下劃線,長(zhǎng)度在6到20之間 return value.matches("^[A-Z][A-Za-z0-9_]{5,19}$"); } }
創(chuàng)建如下注解用于密碼校驗(yàn):
package com.rtxtitanv.annotation; import com.rtxtitanv.validator.RankValidator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.annotation.Rank * @description 自定義用戶段位校驗(yàn)注解 * @date 2021/8/16 15:12 */ @Documented @Constraint(validatedBy = RankValidator.class) @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface Rank { String message() default "rank值無(wú)效"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
創(chuàng)建ConstraintValidator
接口的實(shí)現(xiàn)類并重寫isValid
方法,:
package com.rtxtitanv.validator; import com.rtxtitanv.annotation.Rank; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.HashSet; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.validator.RankValidator * @description RankValidator * @date 2021/8/16 15:18 */ public class RankValidator implements ConstraintValidator<Rank, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { HashSet<String> ranks = new HashSet<>(); ranks.add("無(wú)段位"); ranks.add("青銅"); ranks.add("白銀"); ranks.add("黃金"); ranks.add("鉑金"); ranks.add("鉆石"); // 段位必須為無(wú)段位、青銅、白銀、黃金、鉑金、鉆石之一 return ranks.contains(value); } }
修改User類,使用自定義校驗(yàn)注解:
package com.rtxtitanv.model; import com.rtxtitanv.annotation.Password; import com.rtxtitanv.annotation.Rank; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.*; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.model.User * @description 用戶實(shí)體類 * @date 2021/8/15 16:28 */ @AllArgsConstructor @NoArgsConstructor @Data public class User { @NotNull(message = "id不能為空") private Long id; @Length(min = 6, max = 20, message = "用戶名長(zhǎng)度不小于6,不超過(guò)20") @NotNull(message = "用戶名不能為空") private String username; @Password(message = "密碼必須以大寫英文字母開(kāi)頭,只包含英文字母、數(shù)字、下劃線,長(zhǎng)度在6到20之間") @NotNull(message = "密碼不能為空") private String password; @Max(value = 60, message = "年齡最大為60") @Min(value = 18, message = "年齡最小為18") @NotNull(message = "年齡不能為空") private Integer age; @Email(message = "郵箱格式不正確") @NotEmpty(message = "郵箱不能為空") private String email; @Rank(message = "段位必須為無(wú)段位、青銅、白銀、黃金、鉑金、鉆石之一") private String rank; }
發(fā)送如下POST請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/save
:
八、分組校驗(yàn)
在參數(shù)校驗(yàn)時(shí)如果想針對(duì)不同的方法使用不同的校驗(yàn)規(guī)則,則可以使用分組校驗(yàn)。下面創(chuàng)建兩個(gè)用于分組校驗(yàn)的接口:
public interface AddUserGroup {} public interface ModifyUserGroup {}
在User類的實(shí)例域id
上添加以下注解:
@NotNull(message = "id不能為空", groups = ModifyUserGroup.class) @Null(message = "id必須為空", groups = AddUserGroup.class)
不寫
groups
屬性,則為默認(rèn)分組。
UserController
中新增以下方法:
@PostMapping("/add") public CommonResult<User> addUser(@RequestBody @Validated(value = AddUserGroup.class) User user) { return CommonResult.ok("新增用戶成功", user); } @PutMapping("/modify") public CommonResult<User> modifyUser(@RequestBody @Validated(value = ModifyUserGroup.class) User user) { return CommonResult.ok("修改用戶成功", user); }
發(fā)送如下POST請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/add
:
發(fā)送如下PUT請(qǐng)求,請(qǐng)求地址為http://localhost:8080/user/modify
:
根據(jù)測(cè)試結(jié)果可知,方法addUser
的參數(shù)使用的@Null
校驗(yàn),方法modifyUser
的參數(shù)使用的@NotNull
校驗(yàn),成功實(shí)現(xiàn)了分組校驗(yàn)。
九、嵌套的參數(shù)校驗(yàn)
一個(gè)實(shí)體中嵌套一個(gè)實(shí)體時(shí),通過(guò)在嵌套的實(shí)體類型屬性上添加@Valid
注解,可以對(duì)嵌套的參數(shù)進(jìn)行校驗(yàn)。
Account類:
package com.rtxtitanv.model; import com.rtxtitanv.annotation.Password; import lombok.Data; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.model.Account * @description 賬戶實(shí)體類 * @date 2021/8/17 16:25 */ @Data public class Account { @NotNull(message = "賬戶id不能為空") private Long accountId; @Size(min = 6, max = 20, message = "賬戶名長(zhǎng)度不小于6,不超過(guò)20") @NotNull(message = "賬戶名不能為空") private String accountName; @Password(message = "密碼必須以大寫英文字母開(kāi)頭,只包含英文字母、數(shù)字、下劃線,長(zhǎng)度在6到20之間") @NotNull(message = "賬戶密碼不能為空") private String accountPassword; }
Order類:
package com.rtxtitanv.model; import lombok.Data; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.model.Order * @description 訂單實(shí)體類 * @date 2021/8/17 16:55 */ @Data public class Order { @NotNull(message = "訂單id不能為空") private Long orderId; @NotEmpty(message = "訂單號(hào)不能為空") private String orderNumber; @NotEmpty(message = "訂單描述信息不能為空") private String orderDescription; @Valid private Account account; }
OrderController
:
package com.rtxtitanv.controller; import com.rtxtitanv.model.CommonResult; import com.rtxtitanv.model.Order; 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.Valid; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.controller.OrderController * @description OrderController * @date 2021/8/17 17:00 */ @RequestMapping("/order") @RestController @Validated public class OrderController { @PostMapping("/save") public CommonResult<Order> saveOrder(@RequestBody @Valid Order order) { return CommonResult.ok("訂單保存成功", order); } }
發(fā)送如下POST請(qǐng)求,請(qǐng)求地址為http://localhost:8080/order/save
,發(fā)現(xiàn)嵌套的參數(shù)也被校驗(yàn):
代碼示例
Github:https://github.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-validatorGitee:https://gitee.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-validator
到此這篇關(guān)于SpringBoot2.x 參數(shù)校驗(yàn)的文章就介紹到這了,更多相關(guān)SpringBoot參數(shù)校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?Cookie與Session實(shí)現(xiàn)會(huì)話跟蹤詳解
session的工作原理和cookie非常類似,在cookie中存放一個(gè)sessionID,真實(shí)的數(shù)據(jù)存放在服務(wù)器端,客戶端每次發(fā)送請(qǐng)求的時(shí)候帶上sessionID,服務(wù)端根據(jù)sessionID進(jìn)行數(shù)據(jù)的響應(yīng)2022-11-11java理論基礎(chǔ)函數(shù)式接口特點(diǎn)示例解析
這篇文章主要為大家介紹了java理論基礎(chǔ)函數(shù)式接口特點(diǎn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03Java日期時(shí)間字符串和毫秒相互轉(zhuǎn)換的方法
這篇文章主要為大家詳細(xì)介紹了Java日期時(shí)間字符串和毫秒相互轉(zhuǎn)換的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12HelloSpringMVC注解版實(shí)現(xiàn)步驟解析
這篇文章主要介紹了HelloSpringMVC注解版實(shí)現(xiàn)步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Java之SpringCloud nocos注冊(cè)中心講解
這篇文章主要介紹了Java之SpringCloud nocos注冊(cè)中心講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08Java爬蟲實(shí)現(xiàn)Jsoup利用dom方法遍歷Document對(duì)象
本文主要介紹了Java爬蟲實(shí)現(xiàn)Jsoup利用dom方法遍歷Document對(duì)象,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05四個(gè)實(shí)例超詳細(xì)講解Java?貪心和枚舉的特點(diǎn)與使用
貪心算法是指,在對(duì)問(wèn)題求解時(shí),總是做出在當(dāng)前看來(lái)是最好的選擇。也就是說(shuō),不從整體最優(yōu)上加以考慮,他所做出的是在某種意義上的局部最優(yōu)解,枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡(jiǎn)單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法2022-04-04java中Supplier知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是一篇關(guān)于java中Supplier知識(shí)點(diǎn)總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-04-04