【系统开发】尚硅谷 - 谷粒商城项目笔记(四):JSR303数据校验

news2024/10/7 18:25:32

文章目录

  • JSR303数据校验
    • 引入依赖和简介
    • 配置验证规则
    • 开启验证
    • BindResult
    • 校验的统一异常处理
    • JSR303分组校验
    • 自定义校验注解


JSR303数据校验

引入依赖和简介

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

里面依赖了hibernate-validator 在非空处理方式上提供了@NotNull,@NotBlank和@NotEmpty

1 @NotNull

注解元素禁止为null,能够接收任何类型

2 @NotEmpty

该注解修饰的字段不能为null或""

支持以下几种类型:

  • 字符序列(字符序列长度的计算)

  • 集合长度的计算

  • map长度的计算

  • 数组长度的计算

3 @NotBlank

该注解不能为null,并且至少包含一个非空格字符。接收字符序列。

配置验证规则

/**
 * 品牌
 * @author WSKH
 * @email 1187560563@qq.com
 * @date 2022-01-03 18:28:04
 */
@Data
@ToString
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@TableId
	private Long brandId;
	/**
	 * 品牌名
	 */
	@NotBlank(message = "品牌名必须非空")
	private String name;
	/**
	 * 品牌logo地址
	 */
	@NotEmpty(message = "品牌logo地址不能为空")
//	@URL(message = "品牌logo地址必须是合法的URL")
	private String logo;
	/**
	 * 介绍
	 */
	@NotEmpty(message = "描述信息不能为空")
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@NotNull(message = "显示状态不能为null")
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	@Pattern(regexp = "[a-zA-Z]",message = "检索首字母必须是a-z或A-Z")
	@NotEmpty(message = "检索首字母不能为空")
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(message = "排序信息不能为null")
	private Integer sort;

}

开启验证

在需要验证的controller参数前加上注解,即可开启验证

image-20220127135850296

用postMan进行测试

image-20220127143209899

测试结果如下:

image-20220127143230142

image-20220127143244697

BindResult

给校验的Bean后,紧跟一个BindResult,就可以获取到校验的结果。拿到校验的结果,就可以自定义的封装。

    @RequestMapping("/save")    
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
        if( result.hasErrors()){
            Map<String,String> map=new HashMap<>();
            // 1.获取错误的校验结果
            result.getFieldErrors().forEach((item)->{
                // 2.获取发生错误时的message
                String message = item.getDefaultMessage();
                // 3.获取发生错误的属性名
                String field = item.getField();
                map.put(field,message);
            });
            return R.error(400,"提交的数据不合法").put("data",map);
        }else {
            // 如果校验通过 就进行保存操作
            brandService.save(brand);
            return R.ok();
        }
    }

再用PostMan进行测试看看

image-20220127143749679

比刚刚整齐好看多了!

校验的统一异常处理

可以使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。

0 将原来controller上的BindResult去掉

image-20220127145727887

1 抽取一个异常处理类

// 开启日志功能
@Slf4j
// 使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。
@RestControllerAdvice(basePackages = "com.wskh.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
    @ExceptionHandler(value = Exception.class) // 也可以返回ModelAndView
    public R handleValidException(MethodArgumentNotValidException exception){

        Map<String,String> map=new HashMap<>();
        // 获取数据校验的错误结果
        BindingResult bindingResult = exception.getBindingResult();
        bindingResult.getFieldErrors().forEach(fieldError -> {
            String message = fieldError.getDefaultMessage();
            String field = fieldError.getField();
            map.put(field,message);
        });

        log.error("数据校验出现问题{},异常类型{}",exception.getMessage(),exception.getClass());

        return R.error(400,"数据校验出现问题").put("data",map);
    }

}

2 默认异常处理

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
        log.error("未知异常{},异常类型{}",throwable.getMessage(),throwable.getClass());
        return R.error(400,"数据校验出现问题");
    }

3 错误状态码和错误信息规范化声明

/***
 * 错误码和错误信息定义类
 * 1. 错误码定义规则为5为数字
 * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
 * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
 * 错误码列表:
 *  10: 通用
 *      001:参数格式校验
 *  11: 商品
 *  12: 订单
 *  13: 购物车
 *  14: 物流
 */
public enum BizCodeEnum {

    UNKNOW_EXEPTION(10000,"系统未知异常"),

    VALID_EXCEPTION( 10001,"参数格式校验失败");

    private int code;
    private String msg;

    BizCodeEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

4 使用错误状态码和状态信息

// 开启日志功能
@Slf4j
// 使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。
@RestControllerAdvice(basePackages = "com.wskh.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
    @ExceptionHandler(value = Exception.class) // 也可以返回ModelAndView
    public R handleValidException(MethodArgumentNotValidException exception){

        Map<String,String> map=new HashMap<>();
        // 获取数据校验的错误结果
        BindingResult bindingResult = exception.getBindingResult();
        bindingResult.getFieldErrors().forEach(fieldError -> {
            String message = fieldError.getDefaultMessage();
            String field = fieldError.getField();
            map.put(field,message);
        });

        log.error("数据校验出现问题{},异常类型{}",exception.getMessage(),exception.getClass());

        return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(),BizCodeEnum.VALID_EXCEPTION.getMsg()).put("data",map);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
        log.error("未知异常{},异常类型{}",throwable.getMessage(),throwable.getClass());
        return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),BizCodeEnum.UNKNOW_EXEPTION.getMsg());
    }

}

5 测试下看看

image-20220127145828699

错误状态码和错误信息都已经按照我们自定义的输出了

JSR303分组校验

分组校验常常用于完成多场景的复杂校验

如:指定在更新和添加的时候,都需要进行校验。新增时不需要带id,修改时必须带id

1 在common模块下创建一个文件夹vaild,里面创建两个空的接口AddGroup和UpdateGroup

image-20220127151633431

2 在请求传入参数对象中的校验注解中指定其生效的groups

image-20220127151857050

image-20220127152003390

3 在controller中加入@Validated注解,并指定其对应的分组

image-20220127152144394

4 测试下看看

指定id,测试新增产品

image-20220127152416425

不指定id,测试修改产品

image-20220127152444918

自定义校验注解

场景:要校验showStatus的01状态,可以用正则,但我们可以利用其他方式解决

1 添加依赖

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

2 common模块中新建空接口UpdateStatusGroup,代表只校验showStatus的分组

image-20220127163331496

3 编写自定义的校验器

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    private Set<Integer> set=new HashSet<>();
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] value = constraintAnnotation.value();
        for (int i : value) {
            set.add(i);
        }

    }
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        // 如果所传参数在指定的set集合中,才允许校验通过
        return  set.contains(value);
    }
}

4 编写自定义的校验注解

@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.wskh.common.valid.ListValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    int[] value() default {};
}

5 在common模块的resources文件夹下,创建Validation.properties文件,在里面定义校验失败要返回的msg

image-20220127164144252

6 将注解应用在实例对象的显示状态属性上,并设置分组

image-20220127164353996

7 新增一个只用来修改显示状态的controller方法,分组设置为UpdateStatusGroup.class

    /**
     * 修改显示状态
     */
    @RequestMapping("/update/status")
    // @RequiresPermissions("product:brand:update")
    public R updateStatus(@Validated(UpdateStatusGroup.class) @RequestBody BrandEntity brand){
        brandService.updateById(brand);

        return R.ok();
    }

8 测试一下看看

先试一下不输入显示状态

image-20220127164818786

再试一下输入显示状态为3,会不会成功报错

image-20220127165243112

这里可以看到已经报错了,而且是按照配置文件中的msg来报的,只不过由于项目的编码格式问题,导致出现了乱码

解决方案:在idea设置中,将项目编码格式全部设置为UTF-8

image-20220127165623332

如果改了之后还是这样。那就用英文吧…

image-20220127181709444

image-20220127181702382

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

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

相关文章

Python和c语言爬虫如何选择?

Python是最受欢迎的爬虫语言之一&#xff0c;因为它易于学习和使用&#xff0c;有大量的库和框架可供选择。JavaScript通常用于Web爬虫&#xff0c;因为它可以直接在浏览器中运行&#xff0c;可以轻松地从动态网站中提取数据。java是一种广泛使用的语言&#xff0c;它有很多强大…

提高电商平台精准营销效果的IP定位离线库应用场景

随着电子商务的快速发展&#xff0c;越来越多的人们选择在线购物。随之而来的是消费者数量的增加和商品竞争的激烈。如何精准地找到目标客户&#xff0c;并进行有效的营销&#xff0c;成为了电商平台需要攻克的难题。在这种情况下&#xff0c;IP定位离线库技术的应用成为了电商…

Python基础语法第一章、认识Python

一、计算机基础概念 1.1什么是计算机? 很多老一辈的人, 管下面这个叫做计算机. 然鹅, 它只是 "计算器", 和计算机是有很大区别的. 现在我们所说的计算机, 不光能进行算术运算, 还能进行逻辑判断, 数据存储, 网络通信等等功能, 以至于可以自动的完成非常复杂的工作…

SerDes的原理解析

01 SerDes简介 首先我们要了解什么是SerDes&#xff0c;SerDes的应用场景又是什么呢&#xff1f;SerDes又有哪些常见的种类&#xff1f;做过FPGA的小伙伴想必都知道串口&#xff0c;与并行传输技术相比&#xff0c;串行传输技术的引脚数量少、扩展能力强、采 用点对点的连接方式…

从uCOSii中抠出来的内存管理程序

从uCOSii中抠出来的内存管理程序 1、学习uCOSii的内存管理的原因 操作系统和内存管理是分不开的&#xff0c;每个操作系统都有自己的一套内存管理方法。在实际应用中&#xff0c;我们尽量使用其自带的内存管理。学习和使用uCOSii也有一段时间了&#xff0c;觉得它的内存管理方…

高效处理报表,掌握原生JS打印和导出报表为PDF的顺畅技巧!

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言篇 在日常工作中&#xff0c;报表打印和导出为PDF是经常要处理的任务之一。除了方…

管理类联考——写作——素材篇——论说文——写作素材02——志篇:毅力·坚持

管理类专业学位联考 (写作能力) 论说文素材 02——志篇&#xff1a;毅力坚持 论文说材料: 骐骥一跃&#xff0c;不能十步&#xff1b;驽马十驾&#xff0c;功在不舍。 ——《荀子劝 学》 一&#xff1a;道理论据 咬住青山不放松&#xff0c;立根原在破岩中&#xff1b;千磨…

gitLens插件简单使用(默认上传github)

1.安装 在vscode中的插件管理输入如下后下载 GitLens — Git supercharged 2.配置 点击文件--首选项--设置 点击右上角设置小图标 3.github使用 首先仓库文件一定是要git init是git所管理的 1.在代码文件夹下使用git init创建仓库 2.打开vscode的git管理 3.点击添加暂存区…

如何使用ArcGIS加载天地图

天地图是自然资源部主管&#xff0c;国家基础地理信息中心负责建设的国家地理信息公共服务平台&#xff0c;于2011年1月18日上线。 有的时候可能需要将在线的天地图加载到ArcGIS内&#xff0c;但是加载方式越来越复杂&#xff0c;很多方法都需要申请key&#xff0c;这里为大家…

C++基础(3)——类和对象(2)

前言 本文主要介绍了C中类和对象的基本知识。 4.2.5&#xff1a;深拷贝和浅拷贝 浅拷贝&#xff1a;编译器给我们提供的拷贝函数就是等号复制操作 深拷贝&#xff1a;自己手动重写一个拷贝构造函数&#xff0c;重新new 浅拷贝会出现的问题&#xff1a;如果使用编译器提供的…

AI是什么?AI工具集网站大全

AI是什么&#xff1f; AI 是人工智能的缩写&#xff0c;指的是通过计算机技术和算法来实现智能的能力。我们人类的智能是基于我们的大脑所实现的&#xff0c;而 AI 因此也常被称为机器智能。AI技术需要机器能够感知、推理和行动&#xff0c;这些都需要底层算法的支持&#xff…

2.2C++公有继承与私有继承

C公有继承 C中的公有继承是指一个类可以从另一个类继承公有成员&#xff0c;包括公有成员函数和变量。 公有继承是面向对象编程中最基本的继承方式&#xff0c;它表示父类的公有成员在子类中仍然是公有成员&#xff0c;可以被外部访问。 我写一个 Animal 的基类&#xff0c;…

哪些公司里面有高性能计算方向cuda方向岗位?

CUDA可以为高性能计算、科学计算、深度学习和人工智能、图形渲染和游戏开发、并行数据处理等领域提供了强大的并行计算能力和编程模型。它加速了计算任务的执行&#xff0c;推动了科学研究和创新的进程&#xff0c;同时也为开发者提供了更多的工具和资源&#xff0c;促进了开放…

留个档,Unity Animator state节点的Motion动态替换AnimationClip

前言 由于Unity没有提供直接替换的API&#xff0c;所以在仅限的API下进行逻辑操作。 替换的原理是差不多的&#xff0c;利用AnimatorOverrideController&#xff0c;进行运行时的覆盖。 网上搜索很多文章是利用 名字字符串作为hash的key来进行替换。不满足我自己项目中的需求…

【系统开发】尚硅谷 - 谷粒商城项目笔记(五):分布式缓存

文章目录 分布式缓存缓存使用场景redis作缓存中间件引入redis依赖配置redis堆外内存溢出 缓存失效问题缓存穿透缓存雪崩缓存击穿 Redisson分布式锁导入依赖redisson配置类可重入锁读写锁缓存一致性解决 缓存-SpringCache简介Cacheable自定义缓存配置CacheEvictCachePut原理与不…

【网络】协议的定制与Json序列化和反序列化

文章目录 应用层初识TCP协议通讯流程定制协议再谈协议网络版本计算器Protocal.hppCalServerCalClient Json的安装 应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 初识TCP协议通讯流程 建立链接和断开链接 基于TCP协议&#xff0c…

六、使用深度学习构建人脸识别模型

本章介绍机器学习中人脸识别的历史以及从零开始如何构建一个人脸识别模型,含所有训练数据,源代码,不强制要求GPU。使用 docker 来管理库依赖项,提供与平台无关的一致环境。使用 Dlib 进行预处理,使用 Tensorflow + Scikit-learn 训练能够根据图像预测身份的分类器。 1、人…

《吉林省教育学院学报》简介及投稿邮箱

《吉林省教育学院学报》简介&#xff1a; 主管单位 吉林省教育厅 主办单位 吉林省教育学院 出版周期&#xff1a;月刊 国际刊号&#xff1a;ISSN&#xff1a;1671-1580&#xff1b;国内刊号&#xff1a;CN&#xff1a;22-1296/G4&#xff1b;邮发代号&#xff1a;12-223 出…

创建线程三种方法

创建和运行线程 方法一&#xff0c;直接使用 Thread // 创建线程对象 Thread t new Thread() { public void run() {// 要执行的任务} }; // 启动线程 t.start(); 例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐Thread t1 new Thread("t1"…

Doris的安装

Doris的安装 文章目录 Doris的安装写在前面Linux 操作系统版本需求软件需求操作系统安装要求设置系统最大打开文件句柄数时钟同步关闭交换分区&#xff08;swap&#xff09; 开发测试环境生产环境 安装下载安装包默认端口集群部署前置准备安装部署FE安装部署BE在 **FE** 中添加…