springboot使用注解校验参数 javax.validation 和 hibernate-validator

亦凉 2022-08-29 06:12 720阅读 0赞

springboot使用注解参数校验 javax.validation 和 hibernate-validator

    1. 参数校验概述
    • 1.1 需求概述
    • 1.2 validation与validator
  • 2 注解说明
    • 2.1 validator内置注解
    • 2.2 hibernate validator扩展定义注解
    1. 实现验证
    • 3.1 引入依赖
    • 3.2 代码实现
    • 3.3 实现验证

1. 参数校验概述

1.1 需求概述

常见的业务开发中无可避免的会进行请求参数校验,一般对于复杂的业务参数校验,可以通过校验类单独的校验方法进行处理,通常对于一些与业务无关简单的参数校验可以采用javax.validation 和 hibernate-validator通过注解的方式实现校验。

1.2 validation与validator

javax的validation是Java定义的一套基于注解的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本。
需要注意的是,JSR只是一项标准,它规定了一些校验注解的规范,但没有实现,比如@Null、@NotNull、@Pattern等,它们位于 javax.validation.constraints这个包下。而hibernate validator是对这个规范的实现,并增加了一些其他校验注解,如 @NotBlank、@NotEmpty、@Length等,它们位于org.hibernate.validator.constraints这个包下。

如果我们的项目使用了Spring Boot,hibernate validator框架已经集成在 spring-boot-starter-web中,所以无需再添加其他依赖。如果不是Spring Boot项目,需要添加如下依赖。

  1. <dependency>
  2. <groupId>org.hibernate.validator</groupId>
  3. <artifactId>hibernate-validator</artifactId>
  4. <version>6.0.13.Final</version>
  5. </dependency>

2 注解说明

2.1 validator内置注解






























































注解 说明
@Null 被注释的元素必须为null
@NotNull 被注释的元素不能为null
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min) 被注释的元素的大小必须在指定的范围内
@Digits(integer, fraction) 被注释的元素必须是一个数字,其值必须必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式

2.2 hibernate validator扩展定义注解


































注解 说明
@NotBlank 被注释的元素不能为null,且长度必须大于0,只能用于注解字符串
@NotEmpty 被注释的元素值不为null且不为空,支持字符串、集合、Map和数组类型
@Length(min=,max=) 检查所属的字段的长度是否在min和max之间,只能用于字符串
@Email 被注释的元素必须是电子邮箱地址
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式

注意:
1.正则表达式@Pattern注解只用于String类型的字段上。数字类型的可用@Range注解。
2.需要在Controller中请求对象或者参数前加@Validated或@Valid注解一起使用,
@Validated和@Valid注解区别不是很大,一般情况下任选一个即可,区别如下:





















注解 @Validated @Valid
所属的包 属于org.springframework.validation.annotation包下的,是spring提供的 属于javax.validation包下,是jdk给提供的
是否支持分组和排序

虽然@Validated比@Valid更加强大,在@Valid之上提供了分组功能和验证排序功能,不过在实际项目中一直没有用到过 Hibernate-validate框架中的注解是需要加在实体中一起使用的

3. 实现验证

3.1 引入依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <parent>
  5. <groupId>org.springframework.boot</groupId>
  6. <artifactId>spring-boot-starter-parent</artifactId>
  7. <version>2.5.0</version>
  8. <relativePath/> <!-- lookup parent from repository -->
  9. </parent>
  10. <groupId>com.example</groupId>
  11. <artifactId>category</artifactId>
  12. <version>0.0.1-SNAPSHOT</version>
  13. <name>category</name>
  14. <description>Demo project for Spring Boot</description>
  15. <properties>
  16. <java.version>1.8</java.version>
  17. </properties>
  18. <dependencies>
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-jdbc</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-web</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.mybatis.spring.boot</groupId>
  29. <artifactId>mybatis-spring-boot-starter</artifactId>
  30. <version>2.1.4</version>
  31. </dependency>
  32. <!--数据库连接诶-->
  33. <dependency>
  34. <groupId>mysql</groupId>
  35. <artifactId>mysql-connector-java</artifactId>
  36. <scope>runtime</scope>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.springframework.boot</groupId>
  40. <artifactId>spring-boot-starter-test</artifactId>
  41. <scope>test</scope>
  42. </dependency>
  43. <!--常用工具-->
  44. <dependency>
  45. <groupId>com.google.guava</groupId>
  46. <artifactId>guava</artifactId>
  47. <version>30.1.1-jre</version>
  48. </dependency>
  49. <dependency>
  50. <groupId>cn.hutool</groupId>
  51. <artifactId>hutool-all</artifactId>
  52. <version>5.6.3</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.projectlombok</groupId>
  56. <artifactId>lombok</artifactId>
  57. <optional>true</optional>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.aspectj</groupId>
  61. <artifactId>aspectjweaver</artifactId>
  62. </dependency>
  63. <!--swagger2-->
  64. <dependency>
  65. <groupId>io.springfox</groupId>
  66. <artifactId>springfox-swagger2</artifactId>
  67. <version>2.6.1</version>
  68. </dependency>
  69. <dependency>
  70. <groupId>io.springfox</groupId>
  71. <artifactId>springfox-swagger-ui</artifactId>
  72. <version>2.6.1</version>
  73. </dependency>
  74. <dependency>
  75. <groupId>commons-io</groupId>
  76. <artifactId>commons-io</artifactId>
  77. <version>2.6</version>
  78. </dependency>
  79. <!--字段校验-->
  80. <dependency>
  81. <groupId>javax.validation</groupId>
  82. <artifactId>validation-api</artifactId>
  83. <version>2.0.1.Final</version>
  84. </dependency>
  85. <dependency>
  86. <groupId>org.hibernate.validator</groupId>
  87. <artifactId>hibernate-validator</artifactId>
  88. <version>6.0.13.Final</version>
  89. </dependency>
  90. </dependencies>
  91. <build>
  92. <plugins>
  93. <plugin>
  94. <groupId>org.springframework.boot</groupId>
  95. <artifactId>spring-boot-maven-plugin</artifactId>
  96. </plugin>
  97. </plugins>
  98. </build>
  99. </project>

3.2 代码实现

1.实体类

  1. import io.swagger.annotations.ApiModel;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. import org.hibernate.validator.constraints.Length;
  5. import org.hibernate.validator.constraints.Range;
  6. import javax.validation.constraints.*;
  7. /** * 员工实体类 * * @author zrj * @since 2021/7/31 **/
  8. @Data
  9. @ApiModel(value = "Employee", description = "员工信息")
  10. public class Employee {
  11. @NotNull(message = "userId is empty")
  12. @ApiModelProperty(value = "员工ID")
  13. private Integer id;
  14. @NotBlank(message = "员工姓名不能为空")
  15. @Length(min = 1, max = 100, message = "员工姓名长度不能超过50字")
  16. @ApiModelProperty(value = "员工姓名")
  17. private String name;
  18. @Range(min = 1, max = 200, message = "年龄范围[1,200]")
  19. @ApiModelProperty(value = "年龄")
  20. private Integer age;
  21. @Pattern(regexp = "^[0-1]$", message = "员工类型范围[0,1]")
  22. @ApiModelProperty(value = "员工类型(0行政岗;1基础岗")
  23. private String type;
  24. @Pattern(regexp = "^[1][^0^1^2][0-9]{9}$", message = "员工手机号码不合法")
  25. @ApiModelProperty(value = "员工手机号码")
  26. private String phone;
  27. @Email(message = "员工邮箱不合法")
  28. @Length(min = 1, max = 50, message = "员工邮箱长度")
  29. @ApiModelProperty(value = "员工邮箱")
  30. private String email;
  31. @Length(min = 0, max = 200, message = "备注长度不能超过100字")
  32. @ApiModelProperty(value = "备注")
  33. private String remarks;
  34. }

2.控制类

  1. import com.example.category.entity.Employee;
  2. import com.example.category.entity.Response;
  3. import io.swagger.annotations.Api;
  4. import io.swagger.annotations.ApiOperation;
  5. import org.springframework.web.bind.annotation.*;
  6. import javax.validation.Valid;
  7. /** * 员工控制类 * * @author zrj * @since 2021/7/31 **/
  8. @RestController
  9. @RequestMapping("/employee")
  10. @Api(value = "employee", description = "员工管理")
  11. public class EmployeeController {
  12. @GetMapping("/test")
  13. @ApiOperation(value = "test测试", notes = "test测试", httpMethod = "GET")
  14. public Response test() {
  15. return Response.success("查询成功", null);
  16. }
  17. /** * Valid测试 * 这里职位测试@Valid,所以不在写其他各层 */
  18. @PostMapping("/validEpmloyee")
  19. @ApiOperation(value = "Valid测试", notes = "Valid测试", httpMethod = "POST")
  20. public Response<Employee> validEpmloyee(@Valid @RequestBody Employee employee) {
  21. System.out.println("测试请求对象:" + employee);
  22. if (employee != null) {
  23. return Response.success("查询成功", employee);
  24. }
  25. return Response.fail("查询失败");
  26. }
  27. }

3.结果集响应码

  1. package com.example.category.entity;
  2. import lombok.Data;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Component;
  5. /** * 结果集封装 * * @author zrj * @date 2021/6/2 * @since V1.0 **/
  6. @Data
  7. @Component
  8. public class Response<T> {
  9. private static ResponseCode responseCode;
  10. /** * 提示消息 */
  11. private String message;
  12. /** * 具体返回的数据 */
  13. private T data;
  14. /** * 状态码 */
  15. private String code;
  16. private Response(String code, String message, T data) {
  17. this.message = message;
  18. this.code = code;
  19. this.data = data;
  20. }
  21. private Response(String code, String msg) {
  22. this.message = msg;
  23. this.code = code;
  24. }
  25. @Autowired
  26. public Response(ResponseCode responseCode) {
  27. Response.responseCode = responseCode;
  28. }
  29. /** * 返回成功Response对象 */
  30. public static <T> Response<T> success(String successMessage, T data) {
  31. return new Response<>(responseCode.getSuccessCode(), successMessage, data);
  32. }
  33. /** * 返回错误Response对象 */
  34. public static <T> Response<T> fail(String errorMessage) {
  35. return new Response<>(responseCode.getErrorCode(), errorMessage);
  36. }
  37. }
  38. import lombok.Data;
  39. import org.springframework.stereotype.Component;
  40. /** * 响应码 * * @author zrj * @date 2021/6/2 * @since V1.0 **/
  41. @Data
  42. @Component
  43. public class ResponseCode {
  44. public String successCode = "200";
  45. public String errorCode = "500";
  46. public String authErrorCode = "300";
  47. }

4.异常切面封装类

  1. import com.example.category.entity.Response;
  2. import com.fasterxml.jackson.databind.JsonMappingException;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.http.converter.HttpMessageNotReadableException;
  5. import org.springframework.util.CollectionUtils;
  6. import org.springframework.validation.ObjectError;
  7. import org.springframework.web.bind.MethodArgumentNotValidException;
  8. import org.springframework.web.bind.MissingServletRequestParameterException;
  9. import org.springframework.web.bind.annotation.ControllerAdvice;
  10. import org.springframework.web.bind.annotation.ExceptionHandler;
  11. import org.springframework.web.bind.annotation.ResponseBody;
  12. import org.springframework.web.method.HandlerMethod;
  13. import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
  14. import javax.validation.ConstraintViolation;
  15. import javax.validation.ConstraintViolationException;
  16. import java.util.List;
  17. import java.util.Set;
  18. /** * 异常切面封装类 * * @author zrj * @since 2021/7/31 **/
  19. @ControllerAdvice
  20. @ResponseBody
  21. @Slf4j
  22. public class ExceptionHandlerAdvice {
  23. /** * Exception */
  24. @ExceptionHandler(Exception.class)
  25. public Response<String> handleGlobalException(Exception exception, HandlerMethod handlerMethod) {
  26. if (exception instanceof MethodArgumentNotValidException) {
  27. List<ObjectError> errors = ((MethodArgumentNotValidException) exception).getBindingResult().getAllErrors();
  28. StringBuilder sb = new StringBuilder();
  29. if (!CollectionUtils.isEmpty(errors)) {
  30. for (ObjectError error : errors) {
  31. if (sb.length() != 0) {
  32. sb.append(",");
  33. }
  34. sb.append(error.getDefaultMessage());
  35. }
  36. }
  37. return Response.fail(sb.toString());
  38. }
  39. // 约束异常
  40. if (exception instanceof ConstraintViolationException) {
  41. Set<ConstraintViolation<?>> exceptionSet = ((ConstraintViolationException) exception).getConstraintViolations();
  42. StringBuilder sb = new StringBuilder();
  43. if (!CollectionUtils.isEmpty(exceptionSet)) {
  44. for (ConstraintViolation<?> set : exceptionSet) {
  45. if (sb.length() != 0) {
  46. sb.append(",");
  47. }
  48. sb.append(set.getMessageTemplate());
  49. }
  50. }
  51. return Response.fail(sb.toString());
  52. }
  53. // 参数类型转换异常处理
  54. if (exception instanceof MethodArgumentTypeMismatchException) {
  55. return Response.fail(((MethodArgumentTypeMismatchException) exception).getName() + " 类型不匹配");
  56. }
  57. if (exception instanceof JsonMappingException) {
  58. return Response.fail("JSON格式错误, " + exception.getLocalizedMessage());
  59. }
  60. if (exception instanceof HttpMessageNotReadableException) {
  61. return Response.fail("请求体格式错误, " + exception.getLocalizedMessage());
  62. }
  63. if (exception instanceof MissingServletRequestParameterException) {
  64. String paramName = ((MissingServletRequestParameterException) exception).getParameterName();
  65. return Response.fail(paramName + " 不能为空");
  66. }
  67. //if (exception instanceof MarketingException) {
  68. // MarketingException marketingException = (MarketingException) exception;
  69. // return RdfaResult.fail(marketingException.getErrorCodeEnum().getCode(), exception.getMessage());
  70. //}
  71. // 其他异常打印日志
  72. log.error("{}.{} error, ", handlerMethod.getBeanType().getSimpleName(), handlerMethod.getMethod().getName(), exception);
  73. //if (exception instanceof RpcException) {
  74. // return RdfaResult.fail(ErrorCodeEnum.RPC_ERROR.getCode(), "RPC调用错误,请稍后重试");
  75. //}
  76. return Response.fail("服务器内部错误,请联系开发人员!");
  77. }
  78. }

3.3 实现验证

在这里插入图片描述在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,720人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SpringBoot 注解校验参数

    在项目开发中我们经常会遇到各种参数校验,尤其是表单参数的校验。当参数不多时我们可以在控制器中手动校验,但是一旦遇到需要校验的参数较多的post接口时,还去一个个的校验的话那会累