【Java】Sping Boot中使用Javax Bean Validation

Javax Bean Validation

Javax Bean Validation是Java平台的一项规范,旨在提供一种简单且可扩展的方式来验证Java对象的数据。它提供了一组注解,可以应用于Java Bean的属性上,以定义验证规则,并提供了一组验证器来执行这些规则。

在Spring Boot中集成Javax Bean Validation

本文基于SSM框架(可参考博客:【Java】使用IntelliJ IDEA搭建SSM(MyBatis-Plus)框架并连接MySQL数据库

Spring Boot对Javax Bean Validation提供了内置支持。

在pom.xml文件中添加依赖(包含在标签dependencies中):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>9.0.83</version>
</dependency>

这些依赖会自动包含Javax Bean Validation API以及Hibernate Validator作为实现。
其中spring-boot-starter-validation的版本由其parent确定:

<!-- 定义父项目,使用Spring Boot 的版本管理 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.17</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

使用案例

以用户注册功能为例,验证用户输入的用户名和密码。

  1. 创建一个名为User的Java实体类:
import lombok.Data;
import javax.validation.constraints.Size;
import javax.validation.constraints.NotBlank;

@Data
public class User {

    @NotBlank(message = "用户名不能为空")
    private String username;

    @Size(min = 6, message = "密码长度不能少于6位")
    private String password;
}

在User类中,使用了@NotBlank@Size注解来定义了对用户名和密码的验证规则。

  1. 创建UserMapper,UserMapperxml,UserService,UserServiceImpl
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.z.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.z.mapper.UserMapper">
</mapper>
import com.baomidou.mybatisplus.extension.service.IService;
import com.z.entity.User;

public interface UserService extends IService<User> {
}
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.z.entity.User;
import com.z.mapper.UserMapper;
import com.z.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
  1. 创建一个REST控制器来处理用户注册请求:
import com.z.entity.User;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@RequestBody @Valid User user) {
        // 处理用户注册逻辑
        return ResponseEntity.ok("用户注册成功");
    }
}

在UserController中,使用了@Valid注解来告诉Spring Boot验证User对象,并通过@RequestBody注解将请求体映射到User对象上。

  1. 新建一个数据库及与实体类对应的数据表,并配置数据库:

在resources下添加application.yml文件:

server:
  # 端口
  port: 8080

spring:
  # 数据源配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/yourdatabase?characterEncoding=utf-8
    username: yourusername
    password: yourpassword
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss


mybatis-plus:
  # mapper文件映射路径
  mapper-locations: classpath*:mapper/*.xml
  configuration:
    # 打印SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

其中,yourusername,yourpassword,yourdatabase注意换成自己的。

  1. 编写Main.java运行项目,并通过IDEA的启动按钮启动项目:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

功能测试

使用Postman发送以下JSON请求体:
在这里插入图片描述

点击send,接收到一个400 Bad Request响应:

在这里插入图片描述
可以观察到,之前写在注解中的message并没有展示,需配置全局异常处理器处理异常。

配置全局异常处理器

创建一个@ControllerAdvice类,并在其中定义一个方法来处理MethodArgumentNotValidException异常:

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        StringBuilder errors = new StringBuilder();
        ex.getBindingResult().getFieldErrors().forEach(error -> errors.append(error.getDefaultMessage()).append("\n"));
        return ResponseEntity.badRequest().body(errors.toString());
    }
}

重新测试

重新运行程序测试,并发送请求,得到如下结果:
在这里插入图片描述
可以观察到,这里仅仅是将错误信息以文本形式展示,并没有返回状态码等信息。

返回特定形式的信息

方式一

重写全局异常处理器:

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, Object> body = new HashMap<>();
        body.put("timestamp", LocalDateTime.now());
        body.put("status", 400);
        body.put("error", "Bad Request");

        StringBuilder errors = new StringBuilder();
        ex.getBindingResult().getFieldErrors().forEach(error -> errors.append(error.getDefaultMessage()).append(System.lineSeparator()));
        body.put("message", errors.toString());

        return ResponseEntity.badRequest().body(body);
    }
}

测试结果:
在这里插入图片描述

方式二

  1. 定义一个返回结果类型ApiResult
import lombok.Data;
import java.util.List;

@Data

public class ApiResult {
    // 定义状态码
    public static final int OK = 200;
    public static final int ERROR = 500;
    public static final int Invalid = 400;

    // 定义返回结果的字段
    private int code;
    private String message;
    private Object data;

    // 构造器
    public ApiResult(int code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    // 静态方法创建成功的响应
    public static ApiResult ok(String message, Object data) {
        return new ApiResult(OK, message, data);
    }

    // 静态方法创建错误的响应
    public static ApiResult error(String message) {
        return new ApiResult(ERROR, message, null);
    }

    public static ApiResult violateConstraint(List<String> violation) {
        return new ApiResult(Invalid, "参数校验未通过", violation);
    }
}
  1. 修改全局异常处理器:
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.stream.Collectors;

@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult handleValidationExceptions(MethodArgumentNotValidException ex) {
        List<String> violations = ex.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).
                collect(Collectors.toList());
        return ApiResult.violateConstraint(violations);
    }
}
  1. 修改UserController中注册接口的返回类型:
@RestController
@RequestMapping("/test")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public ApiResult registerUser(@RequestBody @Valid User user) {
        userService.save(user);
        return ApiResult.ok("用户注册成功",user);
    }
}

测试结果:
在这里插入图片描述

附:常用的注解

注解适用类型说明使用案例
@Null任意类型被注释的元素必须为null@Null(message = "必须为null")
@NotNull任意类型被注释的元素不能为null@NotNull(message = "不能为null")
@NotEmpty字符串、集合、数组被注释的元素不能为 null 也不能为空@NotEmpty(message = "不能为null或者为空")
@NotBlank字符串被注释的字符串不能为 null ,且去除空格后的长度不为 0@NotBlank(message = "name为必传参数")
@Size字符串、集合、数组被注解的元素的大小必须在指定的范围内@Size(min = 5,max = 20,message = "字符长度在 5 -20 之间")
@Min数字被注解的元素必须是一个数字,其值必须大于等于指定的最小值@Min(value = 0,message = "最小金额不能小于 0")
@Max数字被注解的元素必须是一个数字,其值必须小于等于指定的最大值@Max(value = 200,message = "最大金额不能超过 200")
@DecimalMin数字被注解的元素必须是一个数字,其值必须大于等于指定的最小值,可用于浮点数比较@DecimalMin(value = "0.1",message = "该参数不能小于 0.1")
@DecimalMax数字被注解的元素必须是一个数字,其值必须小于等于指定的最大值,可用于浮点数比较@DecimalMax(value = "100.4",message = "该参数不能大于 100.4")
@Digits数字被注解的元素必须是一个数字,且其值必须在指定的范围内@Digits(integer = 3,fraction = 2,message = "该参数整数位数不能超出3位,小数位数不能超过2位")
@Negative数字被注释的元素必须是负数@Negative(message = "必须是负数")
@NegativeOrZero数字被注释的元素必须是负数或 0@NegativeOrZero(message = "必须是负数或者为0")
@Positive数字被注释的元素必须是正数@Positive(message = "必须是正数")
@PositiveOrZero数字被注释的元素必须是正数或0@PositiveOrZero(message = "必须是正数或者为0")
@Pattern字符串被注解的字符串必须符合指定的正则表达式@Pattern(regexp = "^1[3456789]\d{9}$",message = "手机号格式不正确")
@Email字符串被注解的元素必须是一个电子邮件地址@Email(message = "email格式错误")
@Future日期、时间被注解的元素必须是一个将来的日期@Future(message = "预约日期要大于当前日期")
@FutureOrPresent日期、时间被注释的元素必须是现在或者未来的日期@FutureOrPresent(message = "预约日要大于当前日期")
@AssertTrue布尔被注解的元素必须是true@AssertTrue(message = "该参数必须为 true")
@AssertFalse布尔被注解的元素必须是false@AssertFalse(message = "该参数必须为 false")
  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值