【Java】Sping Boot中使用Javax Bean Validation

news2024/12/27 13:21:28

目录

  • Javax Bean Validation
  • 在Spring 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);
    }
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1695618.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

提示优化 | PhaseEvo:面向大型语言模型的统一上下文提示优化

【摘要】为大型语言模型 (LLM) 制作理想的提示是一项具有挑战性的任务&#xff0c;需要大量资源和专家的人力投入。现有的工作将提示教学和情境学习示例的优化视为不同的问题&#xff0c;导致提示性能不佳。本研究通过建立统一的上下文提示优化框架来解决这一限制&#xff0c;旨…

隐私是建立人工智能信任的关键

微信关注公众号网络研究观获取更多。 谷歌的 Astra 是其首款人工智能代理 谷歌继续将生成式人工智能融入网络安全 云的复杂性是我们这个时代最大的安全威胁 云安全最受关注的问题&#xff1a;人工智能生成的代码 企业可以从人工智能中获得转型利益&#xff0c;但确保“隐…

Linux基础(六):Linux 系统上 C 程序的编译与调试

本篇博客详细分析&#xff0c;Linux平台上C程序的编译过程与调试方法&#xff0c;这也是我们后续程序开发的基础。 目录 一、第一个hello world程序 1.1 创建.c文件 1.2 编译链接 运行可执行程序 二、编译链接过程 2.1 预编译阶段 2.2 编译阶段 2.3 汇编阶段 2.4 链…

UTC与GPS时间转换-[week, sow]

UTC与GPS时间转换-[week, sow] utc2gpsgps2utc测试参考 Ref: Global Positioning System utc2gps matlab源码 function res utc2gps(utc_t, weekStart)%% parameterssec_day 86400;sec_week 604800;leapsec 18; % 默认周一为一周的开始if nargin < 2weekStart d…

别被“涨价“带跑,性价比才是消费真理

文章来源&#xff1a;全食在线 “再不好好赚钱&#xff0c;连方便面也吃不起了。”这是昨天在热搜下&#xff0c;一位网友的留言。而热搜的内容&#xff0c;正是康师傅方便面即将涨价的消息。 01 传闻初现 昨天上午&#xff0c;朋友圈就有人放出康师傅方便面要涨价的消息&am…

Java | Leetcode Java题解之第112题路径总和

题目&#xff1a; 题解&#xff1a; class Solution {public boolean hasPathSum(TreeNode root, int sum) {if (root null) {return false;}if (root.left null && root.right null) {return sum root.val;}return hasPathSum(root.left, sum - root.val) || has…

Linux 生产跑批脚本解读

1.查看定时任务 2.脚本-目录结构 1&#xff09;config.ini 2&#xff09;run.sh 3.命令解读 1&#xff09;ls -1 路径文件夹 含义&#xff1a;ls -1 /home/oracle/shell/config/ 将文件夹config内的文件全部列出 [oracleneptune config]$ ls -1 /home/oracle/shel…

家政服务,让您的家更温馨

家&#xff0c;是我们生活的港湾&#xff0c;也是我们心灵的归宿。在这个快节奏的时代&#xff0c;每个人都在为了生活而奔波。然而&#xff0c;家务琐事却常常成为我们忙碌生活中的绊脚石。为了解决这个问题&#xff0c;家政行业应运而生&#xff0c;为您的生活带来便利与舒适…

【linux】g++/gcc编译器

目录 背景知识 gcc如何完成 预处理(进行宏替换) 编译&#xff08;生成汇编&#xff09; 汇编&#xff08;生成机器可识别代码&#xff09; 链接&#xff08;生成可执行文件或库文件&#xff09; 在这里涉及到一个重要的概念:函数库 函数库一般分为静态库和动态库两…

使用FP8加速PyTorch训练的两种方法总结

在PyTorch中&#xff0c;FP8&#xff08;8-bit 浮点数&#xff09;是一个较新的数据类型&#xff0c;用于实现高效的神经网络训练和推理。它主要被设计来降低模型运行时的内存占用&#xff0c;并加快计算速度&#xff0c;同时尽量保持训练和推理的准确性。虽然PyTorch官方在标准…

【电路笔记】-二阶滤波器

二阶滤波器 二阶(或双极)滤波器由两个连接在一起的 RC 滤波器部分组成,可提供 -40dB/十倍频程滚降率。 1、概述 二阶滤波器也称为 VCVS 滤波器,因为运算放大器用作压控电压源放大器,是有源滤波器设计的另一种重要类型,因为与我们之前研究过的有源一阶 RC 滤波器一起,…

Git 的安装和使用

一、Git 的下载和安装 目录 一、Git 的下载和安装 1. git 的下载 2. 安装 二、Git 的基本使用-操作本地仓库 1 初始化仓库 1&#xff09;创建一个空目录 2&#xff09;git init 2 把文件添加到版本库 1&#xff09;创建文件 2&#xff09;git add . 3&#xff09;g…

迅睿 CMS 中开启【ionCube 扩展】的方法

有时候我们想要某种功能时会到迅睿 CMS 插件市场中找现有的插件&#xff0c;但会有些担心插件是否适合自己的需求。于是迅睿 CMS 考虑到这一层推出了【申请试用】&#xff0c;可以让用户申请试用 30 天&#xff0c;不过试用是有条件的&#xff0c;条件如下&#xff1a; php 版…

MyBatis复习笔记

3.Mybatis复习 3.1 xml配置 properties&#xff1a;加载配置文件 settings&#xff1a;设置驼峰映射 <settings><setting name"mapUnderscoreToCamelCase" value"true"/> </settings>typeAliases&#xff1a;类型别名设置 #这样在映射…

28. 正定矩阵和最小值

文章目录 1. 概述2. 正定矩阵判定条件3. 举例 1. 概述 正定矩阵这节可以将主元&#xff0c;行列式&#xff0c;特征值&#xff0c;还有不稳定性结合起来。以前我们学的是解决方程 A x b Axb Axb 的问题&#xff0c;现在升级&#xff0c;变成 x T A x b x^TAxb xTAxb &…

html 字体设置 (web端字体设置)

windows自带的字体是有版权的&#xff0c;包括微软雅黑&#xff08;方正&#xff09;、宋体&#xff08;中易&#xff09;、黑体&#xff08;中易&#xff09;等 版权算是个大坑&#xff0c;所谓为了避免版权问题&#xff0c;全部使用开源字体即可 我这里选择的是思源宋体&…

Java进阶学习笔记10——子类构造器

子类构造器的特点&#xff1a; 子类的全部构造器&#xff0c;都会先调用父类的构造器&#xff0c;再执行自己。 子类会继承父类的数据&#xff0c;可能还会使用父类的数据。所以&#xff0c;子类初始化之前&#xff0c;一定先要完成父类数据的初始化&#xff0c;原因在于&…

【pyspark速成专家】7_SparkSQL编程1

目录 一&#xff0c;RDD&#xff0c;DataFrame和DataSet对比 二&#xff0c;创建DataFrame 本节将介绍SparkSQL编程基本概念和基本用法。 不同于RDD编程的命令式编程范式&#xff0c;SparkSQL编程是一种声明式编程范式&#xff0c;我们可以通过SQL语句或者调用DataFrame的相…

2024Spring> HNU-计算机系统-实验4-Buflab-导引+验收

前言 称不上导引了&#xff0c;因为验收已经结束了。主要是最近比较忙&#xff0c;在准备期末考试。周五晚上才开始看实验&#xff0c;自己跟着做了一遍实验&#xff0c;感觉难度还是比bomblab要低的&#xff0c;但是如果用心做的话对于栈帧的理解确实能上几个档次。 实验参考…

ClickHouse 24.4 版本发布说明

本文字数&#xff1a;13148&#xff1b;估计阅读时间&#xff1a;33 分钟 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 新的一个月意味着新版本的发布&#xff01; 发布概要 本次ClickHouse 24.4版本包含了13个新功能&#x1f381;…