• 自动秒收录
  • 软件:1973
  • 资讯:57931|
  • 收录网站:279743|

IT精英团

springboot集成了JSR303参数验证和全局异常处理

springboot集成了JSR303参数验证和全局异常处理

浏览次数:
评论次数:
编辑: 泽洋
信息来源: ITPUB
更新日期: 2022-09-23 01:41:00
摘要

一、前言我们在日常开发中,避不开的就是参数校验,有人说前端不是会在表单中进行校验的吗?在后端中,我们可以直接不管前端怎么样判断过滤,我们后端都需要进行再次判断,为了安全。因为前端很容易拜托,当测试使用

  • 正文开始
  • 相关阅读
  • 推荐作品

00-1010在我们的日常开发中,无法回避参数验证。有人说前端会在表单中检查是吗?在后端不管前端怎么判断都可以直接判断过滤,需要在后端再判断一次,为了安全。因为前端容易上来,测试用PostMan测试的时候,如果后端不验证,不是很乱吗?肯定有很多异常。今天,边肖将学习专门用于参数验证的JSR303。可以算是一个工具!

00-1010JSR-303是JAVA EE 6中的一个子规范,叫做Bean Validation,官方参考实现是Hibernate Validator。Hibernate Validator提供了JSR 303规范中所有内置约束的实现,以及一些额外的约束。

Hibernate官网

官网介绍:

验证数据是一项常见的任务,它发生在从表示层到持久层的所有应用层。通常,在每一层都实现相同的验证逻辑,这既耗时又容易出错。为了避免重复这些验证,开发人员通常将验证逻辑直接捆绑到域模型中,将域类与验证代码混合在一起,验证代码实际上是关于类本身的元数据。

在这里插入图片描述

karta bean Validation 2.0-为实体和方法验证定义的元数据模型和API。默认的元数据源是annotation,它可以通过使用XML来覆盖和扩展元数据。API不依赖于特定的应用层或编程模型。特别是,它不依赖于Web或持久层,可用于服务器端应用程序编程和富客户端Swing应用程序开发人员。

在这里插入图片描述

一、前言

依赖性

groupIdorg.springframework.boot/groupId

artifactId spring-boot-starter-validation/artifactId

/依赖关系

00-1010约束注解名称约束注解说明@ null用于验证对象为空@NotNull用于验证对象不能为空,长度为0的字符串不能被校验。@NotBlank只用于字符串类型,不能为null,trim()后的size0@NotEmpty用于集合类。字符串类不能为空,大小不能为0。但不能检查带空格的字符串@Size用于对象(数组、集合、映射、字符串)是给定范围内的长度@Length用于字符串对象'大小必须在指定范围内@Pattern用于字符串对象'符合正则表达式规则@Email用于字符串对象'符合邮箱格式@Min用于数字和字符串对象'符合指定值@Max用于数字和字符串对象'符合指定值@AssertTrue用于布尔对

象是否为true@AssertFalse用于Boolean对象是否为false

所有的大家参考jar包
在这里插入图片描述

五、@Validated、@Valid区别

@Validated:

  • Spring提供的
  • 支持分组校验
  • 可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
  • 由于无法加在成员属性(字段)上,所以无法单独完成级联校验,需要配合@Valid

@Valid:

  • JDK提供的(标准JSR-303规范)
  • 不支持分组校验
  • 可以用在方法、构造函数、方法参数和成员属性(字段)上
  • 可以加在成员属性(字段)上,能够独自完成级联校验

总结:@Validated用到分组时使用,一个学校对象里还有很多个学生对象需要使用@Validated在Controller方法参数前加上,@Valid加在学校中的学生属性上,不加则无法对学生对象里的属性进行校验!

区别参考博客地址

例子:

@Datapublic class School{    @NotBlank    private String id;    private String name;    @Valid                // 需要加上,否则不会验证student类中的校验注解    @NotNull 			  // 且需要触发该字段的验证才会进行嵌套验证。    private List<Student> list;}@Datapublic class Student {    @NotBlank    private String id;    private String name;    private int age;    }@PostMapping("/test")public Result test(@Validated @RequestBody School school){}

六、常用使用测试

1. 实体类添加校验

import lombok.Data;import javax.validation.constraints.Min;import javax.validation.constraints.NotBlank;import javax.validation.constraints.NotNull;import javax.validation.constraints.Pattern;import java.io.Serializable;@Datapublic class BrandEntity implements Serializable {	private static final long serialVersionUID = 1L;	/**	 * 品牌id	 */	@NotNull(message = "修改必须有品牌id")	private Long brandId;	/**	 * 品牌名F	 */	@NotBlank(message = "品牌名必须提交")	private String name;	/**	 * 品牌logo地址	 */	@NotBlank(message = "地址必须不为空")	private String logo;	/**	 * 介绍	 */	private String descript;		/**	 * 检索首字母	 */	//正则表达式	@Pattern(regexp = "^[a-zA-Z]$",message = "检索的首字母必须是字母")	private String firstLetter;	/**	 * 排序	 */	@Min(value = 0,message = "排序必须大于等于0")	private Integer sort;}

2. 统一返回类型

import com.alibaba.druid.util.StringUtils;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;//统一返回结果@Data@NoArgsConstructor@AllArgsConstructor@ApiModelpublic class Result<T> {    @ApiModelProperty("响应码")    private Integer code;    @ApiModelProperty("相应信息")    private String msg;    @ApiModelProperty("返回对象或者集合")    private T data;    //成功码    public static final Integer SUCCESS_CODE = 200;    //成功消息    public static final String SUCCESS_MSG = "SUCCESS";    //失败    public static final Integer ERROR_CODE = 201;    public static final String ERROR_MSG = "系统异常,请联系管理员";    //没有权限的响应码    public static final Integer NO_AUTH_COOD = 999;    //执行成功    public static <T> Result<T> success(T data){        return new Result<>(SUCCESS_CODE,SUCCESS_MSG,data);    }    //执行失败    public static <T> Result failed(String msg){        msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;        return new Result(ERROR_CODE,msg,"");    }    //传入错误码的方法    public static <T> Result failed(int code,String msg){        msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;        return new Result(code,msg,"");    }    //传入错误码的数据    public static <T> Result failed(int code,String msg,T data){        msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;        return new Result(code,msg,data);    }}

3. 测试类

@PostMapping("/add")public Result add(@Valid @RequestBody BrandEntity brandEntity)  {    return Result.success("成功");}

遇到的坑:小编在公司的项目中添加没什么问题,但是就是无法触发校验,看到的是Springboot版本太高了,所有要添加下面的依赖才触发。

<dependency>    <groupId>org.hibernate.validator</groupId>    <artifactId>hibernate-validator</artifactId>    <version>6.0.18.Final</version></dependency>

4. 普通测试结果

在这里插入图片描述

5. 我们把异常返回给页面

@PostMapping("/add")public Result add(@Valid @RequestBody BrandEntity brandEntity, BindingResult bindingResult){    if (bindingResult.hasErrors()){        Map<String,String> map = new HashMap<>();        bindingResult.getFieldErrors().forEach(item ->{            map.put(item.getField(),item.getDefaultMessage());        });        return Result.failed(400,"提交的数据不合规范",map);    }        return Result.success("成功");}

6. 异常处理结果

{    "code": 400,    "data": {        "name": "品牌名必须提交",        "logo": "地址必须不为空"    },    "msg": "提交的数据不合规范"}

七、抽离全局异常处理

1. 心得体会

上面我们要在每个校验的接口上面写,所以我们要抽离出来做个全局异常。并且要改进一下,原来的是把错误信息放到data里,但是正常情况下的data是返回给前端的数据。我们这样把异常数据放进去,会使data的数据有二义性。这样对于前端就不知道里面是数据还是报错信息了哈,这样就可以直接前端展示msg里面的提示即可!

2. 书写ExceptionControllerAdvice

import com.wang.test.demo.response.Result;import lombok.extern.slf4j.Slf4j;import org.springframework.validation.BindingResult;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;@Slf4j@RestControllerAdvice(basePackages = "com.wang.test.demo.controller")public class ExceptionControllerAdvice {    @ExceptionHandler(value = MethodArgumentNotValidException.class)    public Result handleVaildException(MethodArgumentNotValidException e){        log.error("数据校验出现问题:{},异常类型:{}",e.getMessage(),e.getClass());        BindingResult bindingResult = e.getBindingResult();        StringBuffer stringBuffer = new StringBuffer();        bindingResult.getFieldErrors().forEach(item ->{            //获取错误信息            String message = item.getDefaultMessage();            //获取错误的属性名字            String field = item.getField();            stringBuffer.append(field + ":" + message + " ");        });        return Result.failed(400, stringBuffer + "");    }    @ExceptionHandler(value = Throwable.class)    public Result handleException(Throwable throwable){        log.error("错误",throwable);        return Result.failed(400, "系统异常");    }}

3. 测试结果

{    "code": 400,    "data": "",    "msg": "logo:地址必须不为空 name:品牌名必须提交 "}

八、分组校验

1. 需求

我们在做校验的时候,通常会遇到一个实体类的添加和修改,他们的校验规则是不同的,所以分组显得尤为重要。他可以帮助我们少建一个冗余的实体类,所以我们必须要会的。

2. 创建分组接口(不需写任何内容)

public interface EditGroup {}public interface AddGroup {}

3. 在需要二义性的字段上添加分组

/** * 品牌id */@NotNull(message = "修改必须有品牌id",groups = {EditGroup.class})@Null(message = "新增不能指定id",groups = {AddGroup.class})private Long brandId;// 其余属性我们不变

4. 不同Controller添加校验规则

注意:我们要进行分组,所以@Valid不能使用了,要使用@Validated。相信大家已经看到上面的他俩区别了哈!

@PostMapping("/add")public Result add(@Validated({AddGroup.class}) @RequestBody BrandEntity brandEntity){    return Result.success("成功");}@PostMapping("/edit")public Result edit(@Validated({EditGroup.class}) @RequestBody BrandEntity brandEntity){    return Result.success("成功");}

5. 测试

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

九、自定义校验

1.定义自定义校验器

import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import java.util.HashSet;import java.util.Set;//编写自定义的校验器public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {    private Set<Integer> set=new HashSet<Integer>();    //初始化方法    @Override    public void initialize(ListValue constraintAnnotation) {        int[] value = constraintAnnotation.vals();        for (int i : value) {            set.add(i);        }    }    /**     * 判断是否校验成功     * @param value  需要校验的值     * @param context     * @return     */    @Override    public boolean isValid(Integer value, ConstraintValidatorContext context) {        return  set.contains(value);    }}

2. 定义一个注解配合校验器使用

@Documented@Constraint(validatedBy = { ListValueConstraintValidator.class })@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)public @interface ListValue {    // 使用该属性去Validation.properties中取    String message() default "{com.atguigu.common.valid.ListValue.message}";    Class<?>[] groups() default { };    Class<? extends Payload>[] payload() default { };    int[] vals() default {};}

3. 实体类添加一个新的校验属性

注意:我们上面做了分组,如果属性不指定分组,则不会生效,现在我们的部分属性校验已没有起作用,现在只有brandId和showStatus起作用。

/** * 显示状态[0-不显示;1-显示] */@NotNull(groups = {AddGroup.class, EditGroup.class})@ListValue(vals = {0,1},groups = {AddGroup.class, EditGroup.class},message = "必须为0或者1")private Integer showStatus;

4. 测试

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

十、总结

这样就差不多对JSR303有了基本了解,满足基本开发没有什么问题哈!看到这里了,收藏点赞一波吧,整理了将近一天!!谢谢大家了!!

标签:对象 属性 数据
Java开发学习- SpringBoot快速入门和启动依赖分析
« 上一篇 2022-09-23
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
  • spring接口有多个实现类 应该给哪个注入这个依赖?
    0阅读 0条评论 个赞
    一、问题的描述在实际的系统应用开发中我经常会遇到这样的一类需求,相信大家在工作中也会经常遇到:同一个系统在多个省份部署。一个业务在北京是一种实现方式,是基于北京用户的需求。同样的业务在上海是另外一种实……
  • 用户自定义注释 AOP实现的日志保存(数据库) 所有代码都可以粘贴复制
    0阅读 0条评论 个赞
    前言1,在一些特定的场景我们往往需要看一下接口的入参,特别是跨系统的接口调用(下发,推送),这个时候的接口入参就很重要,我们保存入参入库,如果出问题就可以马上定位是上游还是下游的问题(方便扯皮)2,还……
  • SQL Server批量完整备份
    0阅读 0条评论 个赞
    一.本文所涉及的内容(Contents)本文所涉及的内容(Contents)背景(Contexts)实现代码(SQLCodes)实现方式一(One)实现方式二(Two)实现方式三(Three)参考文……
  • 大促销活动如何抵御高流量DDoS攻击?
    0阅读 0条评论 个赞
    大促活动如何抵御大流量DDoS攻击?每一次活动大促带来的迅猛流量,对技术人而言都是一次严峻考验。如果在活动期间遭受黑产恶意DDoS攻击,无疑是雪上加霜。电商的特性是业务常态下通常不会遭受大流量DD……
  • Python数据分析教程(一):Numpy
    0阅读 0条评论 个赞
    数据的纬度一维数据:列表和集合类型二维数据:列表类型多维数据:列表类型高维数据:字典类型或数据表示格式,如json、xml、yaml维度:一组数据的组织形式列表和数组:一组数据的有序结构NumpyNu……
  • 中国台湾的建设:有效登陆中国台湾的六脉神剑
    0阅读 0条评论 个赞
    在数字化时代,数字化体系的建设需要的是系统化的规划和产品化的迭代的模式,基于企业核心业务能力体系,做中台化的持续建设与落地,则是一种不错的选择。所以,企业业务中台的建设和落地,是关系到企业数字化战略成……
  • 数据库发展史1-传统数据库
    0阅读 0条评论 个赞
    1946年,美国宾夕法尼亚大学诞生了人类第一台电子计算机--ENIAC(ElectronicNumericalIntegratorAndComputer,即电子数字积分计算机),这个占地170……
  • 基于Flyway的数据库版本控制实践
    0阅读 0条评论 个赞
    背景大家平时在开发过程中,会用Git来进行我们的代码管理。如Git这些,使用这些版本控制系统能轻松的帮我们解决不同开发人员之间的代码冲突处理版本回退实现软件代码的CI/CD等那大家考虑过么,针对数据库……
  • 教你如何构建JAVA分布式爬虫
    0阅读 0条评论 个赞
    在工作中,我们经常需要去获取一些数据,但是这些数据可能需要从第三方平台才可以获取到。这个时候,爬虫系统就可以帮助我们来完成这些事情。提到爬虫系统,很多人都会想到使用python。但实际上,语言只……
  • SQL Server复制:事务发布
    0阅读 0条评论 个赞
    一、背景在复制的运用场景中,事务发布是使用最为广泛的,我遇到这样一个场景:在Task数据库中有Basic与Group两个表,需要提供这两个表的部分字段给其它程序读取放入缓存,程序需要比较及时的获取……
  • SQL Server数据库性能优化
    0阅读 0条评论 个赞
    分析比较执行时间计划读取情况1.查看执行时间和cpusetstatisticstimeonselect*fromBus_DevHistoryDatasetstatisticstime……
  • 谈ASP.NET核心认证与授权
    0阅读 0条评论 个赞
    使用asp.netcore开发应用系统过程中,基本上都会涉及到用户身份的认证,及授权访问控制,因此了解认证和授权流程也相当重要,下面通过分析asp.netcore框架中的认证和授权的源码来分析……
  • 高手面试一个人 问4个问题就够了
    0阅读 0条评论 个赞
    作者|Mr.K编辑|Emma来源|技术领导力(ID:jishulingdaoli)金九银十求职季又要来了。据统计,今年的应届毕业生已破千万,加上社会面存量人才,相信今年的人才季的热度,不会低于今年……
  • Linux环境程序如何运行?
    0阅读 0条评论 个赞
    .css-1yuhvjn{margin-top:16px;}.css-3jt6os.FileLinkCard{-webkit-align-items:center;-webkit-box-align……
  • 能够在JAVA中自定义和扩展Swagger 自动生成参数值的含义 提高开发效率
    0阅读 0条评论 个赞
    大家好,又见面了。在JAVA做前后端分离的项目开发的时候,服务端需要提供接口文档供周边人员做接口的对接指导。越来越多的项目都在尝试使用一些基于代码自动生成接口文档的工具来替代由开发人员手动编写接口文档……
  • 深入了解美团叶子发射器开源方案
    0阅读 0条评论 个赞
    大家好,我是树哥。之前我们有聊过「如何设计一个分布式ID发号器」,其中有讲过4种解决方案,分别是:UUID类雪花算法数据库自增主键Redis原子自增美团以第2、3种解决方案为基础,开发出……
  • SQL Server合并(删除)分区的歧义消除
    4阅读 0条评论 个赞
    一、准备在SQLServer2005版本之后就有了表分区的概念与应用,在分区操作里面有一个叫做合并分区的功能,也被称为删除分区。分区所处的文件组和文件是不会被删除的,只会对数据进行转移合并。合并分……
  • 如何优雅地转换Bean对象
    0阅读 0条评论 个赞
    背景我们的故事要从一个风和日丽的下午开始说起!这天,外包韩在位置上写代码~外包韩根据如下定义PO(persistantobject):持久化对象,可以看成是与数据库中的表相映射的java对象。最……
  • RDD星火基金简介(01)
    0阅读 0条评论 个赞
    1,基本概念RDD(ResilientDistributedDataset):弹性分布式数据集它是Spark中最基本的数据抽象,是编写Spark程序的基础。简单的来讲,一个Spark程序可以概括……
  • 用于访问大型应用程序的应用程序启动流程框架
    0阅读 0条评论 个赞
    对于大型的应用软件,特别是客户端应用软件,应用启动过程中,需要执行大量的逻辑,包括各个模块的初始化和注册等等逻辑。大型应用软件的启动过程都是非常复杂的,而客户端应用软件是对应用的启动性能有所要求的,不……
最近发布资讯
更多