SpringBoot2.x 参数校验问题小结

这篇文章主要介绍了SpringBoot2.x 参数校验一些问题总结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

本文主要对SpringBoot2.x参数校验进行简单总结,其中SpringBoot使用的2.4.5版本。

一、引入依赖

  org.springframework.bootspring-boot-starter-web org.springframework.bootspring-boot-starter-validation org.projectlomboklombok1.18.8

二、实体类

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 用户实体类 * @date 2021/8/15 16:28 */ @AllArgsConstructor @NoArgsConstructor @Data public class User { @NotNull(message = "id不能为空") private Long id; @Length(min = 6, max = 20, message = "用户名长度不小于6,不超过20") @NotNull(message = "用户名不能为空") private String username; @Pattern(regexp = "^[A-Z][A-Za-z0-9_]{5,19}$", message = "密码以大写英文字母开头,只包含英文字母、数字、下划线,长度在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; }

通用响应类:

 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 通用响应类 * @date 2021/8/15 17:35 */ @AllArgsConstructor @NoArgsConstructor @Data public class CommonResult implements Serializable { private static final long serialVersionUID = 5231430760082814286L; private int code; private String message; private T data; public static  CommonResult ok(String message, T data) { return new CommonResult(HttpStatus.OK.value(), message, data); } public static  CommonResult fail(int code, String message, T data) { return new CommonResult<>(code, message, data); } }

三、常用的校验注解

这里对一些用于参数校验的常用注解进行总结:

  • @Null:必须为null。
  • @NotNull:必须不为null。
  • @AssertTrue:必须为true。
  • @AssertFalse:必须为false。
  • @Min(value):必须是一个大于等于指定值的数字。
  • @Max(value):必须是一个小于等于指定值的数字。
  • @DecimalMin(value):必须是一个大于等于指定值的数字。
  • @DecimalMax(value):必须是一个小于等于指定值的数字。
  • @Size(min=,max=):大小必须在指定的范围内。
  • @Digits(integer, fraction):必须是一个数字,其值必须在可接受的范围内。
  • @Past:必须是一个过去的日期。
  • @Future:必须是一个将来的日期。
  • @Pattern(regex=):必须符合指定的正则表达式。
  • @Email:必须是一个有效的email地址。
  • @Length(min=,max=):字符串长度是否在指定范围内。
  • @NotBlank:必须非空且长度大于0。
  • @NotEmpty:必须不为null或空。
  • @URL(protocol=,host,port):必须是一个有效的URL,如果提供了protocol,host等,则还需满足提供的条件。

四、校验Controller中的参数

1.校验请求体

创建UserController,在需要校验的参数上添加@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 saveUser(@RequestBody @Valid User user) { return CommonResult.ok("保存用户成功", user); } }

启动项目,发送如下POST请求,请求地址为http://localhost:8080/user/save

1

如果验证失败会抛出MethodArgumentNotValidException,默认情况下,Spring会将MethodArgumentNotValidException异常转换为HTTP Status 400。查看控制台打印的日志发现抛出了MethodArgumentNotValidException

2

创建全局异常处理类捕获异常并进行处理:

 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> validateException(MethodArgumentNotValidException e) { Map errors = new HashMap<>(16); e.getBindingResult().getAllErrors() .forEach(error -> errors.put(((FieldError)error).getField(), error.getDefaultMessage())); return CommonResult.fail(HttpStatus.BAD_REQUEST.value(), "无效的参数", errors); } }

重启项目后再次发送如下POST请求:

3

2.校验请求参数

UserController上添加Validated注解并新增以下方法:

 @GetMapping("/get/{id}") public CommonResult 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("根据id查询用户成功", user); } @DeleteMapping("/delete") public CommonResult deleteByUsername( @Valid @RequestParam(value = "username") @Size(min = 6, max = 20, message = "用户名长度不在指定范围内") String username) { User user = new User(1L, username, "A123456sd", 20, "zhaoyun123@xxx.com", "黄金"); return CommonResult.ok("根据用户名删除用户成功", user); }

全局异常处理类中新增以下方法:

 @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR) public CommonResult> handleConstraintViolationException(ConstraintViolationException e) { Map errors = new HashMap<>(16); e.getConstraintViolations().forEach(constraintViolation -> errors .put(constraintViolation.getPropertyPath().toString(), constraintViolation.getMessage())); return CommonResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "无效的参数", errors); }

发送如下GET请求,请求地址为http://localhost:8080/user/get/0

4

发送如下DELETE请求,请求地址为http://localhost:8080/user/delete?username=ZhaoZiLong_1896582826

5

五、校验Service中的参数

通过@Validated@Valid注解组合使用不仅可以校验Controller中的参数,还可以校验任何Spring组件中的参数。

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 用户参数 * @return CommonResult */ CommonResult updateUser(@Valid User user); }

UserService实现类:

 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实现类 * @date 2021/8/15 16:46 */ @Service public class UserServiceImpl implements UserService { @Override public CommonResult updateUser(User user) { return CommonResult.ok("更新用户成功", user); } }

UserController中新增以下代码:

 @Resource private UserService userService; @PutMapping("/update") public CommonResult updateUser(@RequestBody User user) { return userService.updateUser(user); }

发送如下PUT请求,请求地址为http://localhost:8080/user/update

6

六、编程式校验

通过Validator实例可以手动进行参数校验。UserController中新增以下代码:

 @Resource private Validator validator; @PostMapping("/insert") public CommonResult insertUser(@RequestBody User user) { if (!validator.validate(user).isEmpty()) { throw new ConstraintViolationException(validator.validate(user)); } return CommonResult.ok("添加用户成功", user); }

发送如下POST请求,请求地址为http://localhost:8080/user/insert

7

七、自定义校验注解

如果自带的校验注解无法满足需求,还可以自定义校验注解。首先创建如下注解用于密码校验:

 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 自定义密码校验注解 * @date 2021/8/16 14:54 */ @Documented @Constraint(validatedBy = PasswordValidator.class) @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Password { String message() default "无效密码"; Class[] groups() default {}; Class[] payload() default {}; }

然后创建ConstraintValidator接口的实现类并重写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 { @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) { return true; } // 密码必须以大写英文字母开头,只包含英文字母、数字、下划线,长度在6到20之间 return value.matches("^[A-Z][A-Za-z0-9_]{5,19}$"); } }

创建如下注解用于密码校验:

 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 自定义用户段位校验注解 * @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值无效"; Class[] groups() default {}; Class[] payload() default {}; }

创建ConstraintValidator接口的实现类并重写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 { @Override public boolean isValid(String value, ConstraintValidatorContext context) { HashSet ranks = new HashSet<>(); ranks.add("无段位"); ranks.add("青铜"); ranks.add("白银"); ranks.add("黄金"); ranks.add("铂金"); ranks.add("钻石"); // 段位必须为无段位、青铜、白银、黄金、铂金、钻石之一 return ranks.contains(value); } }

修改User类,使用自定义校验注解:

 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 用户实体类 * @date 2021/8/15 16:28 */ @AllArgsConstructor @NoArgsConstructor @Data public class User { @NotNull(message = "id不能为空") private Long id; @Length(min = 6, max = 20, message = "用户名长度不小于6,不超过20") @NotNull(message = "用户名不能为空") private String username; @Password(message = "密码必须以大写英文字母开头,只包含英文字母、数字、下划线,长度在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 = "段位必须为无段位、青铜、白银、黄金、铂金、钻石之一") private String rank; }

发送如下POST请求,请求地址为http://localhost:8080/user/save

8

八、分组校验

在参数校验时如果想针对不同的方法使用不同的校验规则,则可以使用分组校验。下面创建两个用于分组校验的接口:

 public interface AddUserGroup {} public interface ModifyUserGroup {}

在User类的实例域id上添加以下注解:

 @NotNull(message = "id不能为空", groups = ModifyUserGroup.class) @Null(message = "id必须为空", groups = AddUserGroup.class)

不写groups属性,则为默认分组。

UserController中新增以下方法:

 @PostMapping("/add") public CommonResult addUser(@RequestBody @Validated(value = AddUserGroup.class) User user) { return CommonResult.ok("新增用户成功", user); } @PutMapping("/modify") public CommonResult modifyUser(@RequestBody @Validated(value = ModifyUserGroup.class) User user) { return CommonResult.ok("修改用户成功", user); }

发送如下POST请求,请求地址为http://localhost:8080/user/add

9

发送如下PUT请求,请求地址为http://localhost:8080/user/modify

10

根据测试结果可知,方法addUser的参数使用的@Null校验,方法modifyUser的参数使用的@NotNull校验,成功实现了分组校验。

九、嵌套的参数校验

一个实体中嵌套一个实体时,通过在嵌套的实体类型属性上添加@Valid注解,可以对嵌套的参数进行校验。

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 账户实体类 * @date 2021/8/17 16:25 */ @Data public class Account { @NotNull(message = "账户id不能为空") private Long accountId; @Size(min = 6, max = 20, message = "账户名长度不小于6,不超过20") @NotNull(message = "账户名不能为空") private String accountName; @Password(message = "密码必须以大写英文字母开头,只包含英文字母、数字、下划线,长度在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 订单实体类 * @date 2021/8/17 16:55 */ @Data public class Order { @NotNull(message = "订单id不能为空") private Long orderId; @NotEmpty(message = "订单号不能为空") 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 saveOrder(@RequestBody @Valid Order order) { return CommonResult.ok("订单保存成功", order); } }

发送如下POST请求,请求地址为http://localhost:8080/order/save,发现嵌套的参数也被校验:

11

代码示例

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

以上就是SpringBoot2.x 参数校验问题小结的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java