关键词:
文章目录
①. JSR303校验概述、注解
-
①. JSR是Java Specification Requests的缩写,意思是Java规范提案,JSR-303是JAVA EE6中的一项子规范,叫做Bean Validation即,JSR 303,Bean Validation规范 ,为Bean验证定义了元数据模型和API。默认的元数据模型是通过Annotations来描述的,但是也可以使用XML来重载或者扩展。
-
②. 如何使用
在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints”包中,提供了如@Email,@NotNull等注解
<!--jsr3参数校验器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- ③. 常用的注解说明
注解 | 解释 |
---|---|
@NotNull | 属性不能为null,无法查检长度为0的字符串 |
@NotBlank | 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格 |
@NotEmpty | 该字段不能为null或"" |
被注释的元素必须是电子邮箱地址 | |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@Range | 被注释的元素必须在合适的范围内 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
②. 项目中JSR303如何使用
- ①. 在类中使用JSR303注解
在message写上我们自己的内容,这样通过测试就返回的数据就是我们自己填写的
@Data
@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
@URL(message = "logo必须是一个合法的url地址")
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty
@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是一个字母")
private String firstLetter;
/**
* 排序
*/
@NotNull
@Min(value = 0)
private Integer sort;
- ②. controller中加校验注解@Valid,开启校验
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand)
brandService.save(brand);
return R.ok();
- ③. 第一次测试:http://localhost:88/api/product/brand/save
在postman种发送上面的请求,可以看到返回的甚至不是R对象
"timestamp": "2020-04-29T09:36:04.125+0000",
"status": 400,
"error": "Bad Request",
"errors": [
"codes": [
"NotBlank.brandEntity.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
"codes": [
"brandEntity.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
],
"defaultMessage": "品牌名称不能为空",
"objectName": "brandEntity",
"field": "name",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotBlank"
],
"message": "Validation failed for object='brandEntity'. Error count: 1",
"path": "/product/brand/save"
- ④. 修改代码,进行第二次测试
- 给校验的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)->
//获取发生错误时的message
String message = item.getDefaultMessage();
//获取发生错误的字段
String field = item.getField();
map.put(field,message);
);
return R.error(400,"提交的数据不合法").put("data",map);
brandService.save(brand);
return R.ok();
③. 统一异常处理@ExceptionHandler
- ①. 抽取一个异常处理类
- @ControllerAdvice标注在类上,通过“basePackages”能够说明处理哪些路径下的异常。
- @ExceptionHandler(value = 异常类型.class)标注在方法上4
- @RestControllerAdvice=@ControllerAdvice+@ResponseBody
- ②. 代码展示 掌握
@Slf4j
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")//管理的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);
- ③. 测试 http://localhost:88/api/product/brand/save
- ④. 默认异常处理
//如果在异常类上这些都没有进行匹配,最后都会由这个异常进行处理
@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());
④. 处理错误状态码
-
①. 上面代码中,针对于错误状态码,是我们进行随意定义的,然而正规开发过程中,错误状态码有着严格的定义规则,如该在项目中我们的错误状态码定义
上面的用法主要是通过@RestControllerAdvice+@ExceptionHandler来进行异常拦截处理 -
②. 为了定义这些错误状态码,我们可以单独定义一个常量类,用来存储这些错误状态码
package com.atguigu.common.exception;
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*/
public enum BizCodeEnum
UNKNOW_EXEPTION(10000,"系统未知异常"),
VALID_EXCEPTION( 10001,"参数格式校验失败");
private Integer code;
private String msg;
BizCodeEnum(Integer code,String msg)
this.code=code;
this.msg=msg;
public int getCode()
return code;
public String getMsg()
return msg;
- ③. 修改处理异常类代码
/**
* 集中处理所有的异常
*/
@Slf4j
//@ControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
//@RestControllerAdvice=@ControllerAdvice+@ResponseBody
public class GulimallExceptionControllerAdvice
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException exception)
log.error("数据校验出现问题,异常类型", exception.getMessage(), exception.getClass());
if (exception.getBindingResult().hasErrors())
Map<String,String>map=new HashMap<>();
exception.getBindingResult().getFieldErrors().forEach((result)->
map.put(result.getField(),result.getDefaultMessage());
);
return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(),BizCodeEnum.VALID_EXCEPTION.getMsg()).put("data",map);
return R.error();
- ④. 测试: http://localhost:88/api/product/brand/save
⑤. 分组校验功能(多场景校验)
-
①. 前面解决了统一异常处理,但是现状有新的需求是对同一实体类参数也要区分场景
如果新增和修改两个接口需要验证的字段不同,比如id字段,新增可以不传递,但是修改必须传递id,我们又不可能写两个vo来满足不同的校验规则。所以就需要用到分组校验来实现。 -
②.分组校验步骤:
- 创建分组接口AddGroup.java和UpdateGroup.java
- 在VO的属性中标注@NotBlank等注解,并指定要使用的分组,如@NotNull(message = “用户姓名不能为空”,groups = AddGroup.class,UpdateGroup.class)
(这个意思是:在新增、修改的时候该属性不能为空) - controller的方法上或者方法参数上写要处理的分组的接口信息,如@Validated(AddGroup.class)
/**
* 品牌id
*/
@NotNull(message ="修改必须指定品牌id",groups =UpdateGroup.class )
@Null(message ="新增不能指定品牌id",groups =AddGroup.class )
@TableId
private Long brandId;
/**
* 品牌logo地址 修改可以不带上logoURL
*/
//注意下面因为@NotBlank没有指定UpdateGroup分组,所以不生效。
//此时update时可以不携带,但带了一定得是合法的url地址
@NotBlank(groups = AddGroup.class)
@URL(message = "logo必须是一个合法的URL地址", groups=AddGroup.class, UpdateGroup.class)
private String logo;
@RequestMapping("/save")
//public R save(@Valid @RequestBody BrandEntity brand/*, BindingResult result*/)
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand/*, BindingResult result*/)
// if(result.hasErrors())
// Map<String,Object>map=new HashMap<>();
// //1.获取错误的校验结果
// result.getFieldErrors().forEach((item)->
// //获取发生错误的字段
// String field = item.getField();
// //获取发生错误时的message
// String message = item.getDefaultMessage();
// map.put(field,message);
// );
// return R.error(400,"提交的数据不合法").put("data",map);
//
brandService.save(brand);
return R.ok();
⑥. 自定义校验注解
- ①. 场景:要校验showStatus的0/1状态,可以用正则,但我们可以利用其他方式解决复杂场景。比如我们想要下面的场景,当前台输入0或者1的时候是合法的,当前台输入不是0或者1就要给出相对于的提示
/**
* 显示状态[0-不显示;1-显示]
*/
//自定义注解
@ListValue(vals=0,1,groups = AddGroup.class)
private Integer showStatus;
- ②. 导入依赖,自定义校验注解有三步:
- 编写一个自定义的校验注解
- 编写一个自定义的校验器ConstraintValidator
- 关联自定义的校验器和自定义的校验注解
- 注意ListValueConstraintValidator.class(可以指定多个不同的校验器,适配不同类型的校验)
<!--校验-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
- ③. 自定义校验注解,必须有3个属性
(@Constraint(validatedBy = ListValueConstraintValidator.class) ListValueConstraintValidator会在第二步进行定义)
- message()错误信息
- groups()分组校验
- payload()自定义负载信息
- 因为上面的message值对应的最终字符串需要去ValidationMessages.properties中获得,所以我们在common中新建文件ValidationMessages.properties(如果出现了乱码删除文件重新定义)
@Documented
@Constraint(validatedBy = ListValueConstraintValidator.class)
@Target(ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue
String message() default "com.atguigu.common.valid.ListValue.message";
Class<?>[] groups() default ;
Class<? extends Payload>[] payload() default ;
int []vals()default ;
com.atguigu.common.valid.ListValue.message=必须提交指定的值[0,1]
- ④. 自定义校验器ConstraintValidator
- 上面只是定义了异常消息,但是怎么验证是否异常还没说,下面的ConstraintValidator就是说的比如我们要限定某个属性值必须在一个给定的集合里,那么就通过重写initialize()方法,指定可以有哪些元素而controller接收到的数据用isValid(验证)
- 具体的校验类需要实现ConstraintValidator接口,第一个泛型参数是所对应的校验注解类型,第二个是校验对象类型。在初始化方法initialize中,我们可以先做一些别的初始化工作,例如这里我们获取到注解上的value并保存下来,然后生成set对象。
- 真正的验证逻辑由isValid完成,如果传入形参的属性值在这个set里就返回true,否则返回false
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> //<注解,校验值类型>
private Set<Integer> set = new HashSet<>();
//初始化方法
@Override
// 存储所有可能的值
public void initialize(ListValue constraintAnnotation)
// 获取后端写好的限制 // 这个value就是ListValue里的value,我们写的注解是@ListValue(value=0,1)
int[] vals = constraintAnnotation.vals();
for (int val : vals)
set.add(val);
//判断是否校验成功
/**
*
* @param value 需要校验的值
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context)
// 看是否在限制的值里
return set.contains(value);
- ⑤. 关联校验器和校验注解(一个校验注解可以匹配多个校验器)
@Constraint(validatedBy = ListValueConstraintValidator.class)
- ⑥. 使用实例
/**
* 显示状态[0-不显示;1-显示]
*/
//自定义注解
@ListValue(vals=0,1,groups = AddGroup.class)
private Integer showStatus;
- ⑦. 使用postman进行测试,当我们将showStatus字段输入数字3,显示乱码。
第185天学习打卡(项目谷粒商城27jsr303自定义校验注解spusku属性分组效果前端组件抽取父子组件交互)(代码片段)
JSR303自定义校验注解gulimall-commonvalidListValue.javapackagecom.doudou.common.valid;importjavax.validation.Constraint;importjavax.validation.Payload;importjava.lang.annotation.*;@Documented@Constrai 查看详情
谷粒商城_06_jsr303校验+elasticsearch(代码片段)
...引删除索引6、ES的批量操作——bulk7、样本测试数据谷粒商城_01_环境搭建谷粒商城_02_Nacos、网关谷粒商城_03_前端基础谷粒商城_04_商品CRUD谷粒商城_05_阿里云OSS和前端校验JSR303校验问题引入:填写form时应该有前端校验,后... 查看详情
第184天学习打卡(项目谷粒商城26统一异常管理jsr303分组校验)(代码片段)
API品牌管理-统一异常管理gulimall-productcontrollerBrandController.javapackagecom.doudou.gulimall.product.controller;importjava.util.Arrays;importjava.util.HashMap;importjava.util.Map;//importorg.apache.shiro.au 查看详情
jsr303数据校验
...比较简便。在javax.validation.constraints包下有许多的注解:常用的校验注解补充:@NotBlank检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.@NotEmpty检查约束元素是否为NULL或者是EMPTY.@Length被检查的字... 查看详情
谷粒商城-jsr303分组校验(代码片段)
一、给校验注解,标注上groups,指定什么情况下才需要进行校验如:指定在更新和添加的时候,都需要进行校验,我们对id进行限制 /** *品牌id */ @NotNull(message="修改必须指定品牌id",groups=UpdateGroup... 查看详情
使用 Spring 的 JSR-303 约束验证器中的依赖注入失败
...相同的问题,但还没有找到解决方案。所以我的示例测试项目将显示整个相关配置和代码:约束注解:@Target(ElementType.METHOD,Ele 查看详情
@validated注解怎么用
现在基本上都是前后端分离项目,后端返回json数据给前端,前端传参一般是也是传json。后台controller用@RequestBody修饰,如示例,如果不使用@Validated那么service就得判断账号密码是否为空,在很多场景下,前端会传大量参数过来,这个... 查看详情
kotlin 数据类 + bean 验证 jsr 303
...-2105:46:39【问题描述】:我正在尝试让Kotlin在spring-data-rest项目中使用jsr303验证。给定以下数据类声明:@EntitydataclassUser(@Id@GeneratedValue(strategy=javax.persistence.Ge 查看详情
springmvc中的jsr303数据校验框架说明
...。JSR303通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证:---------------------------------------------------------------------------注解说明------ 查看详情
实战分析:springboot项目jsr303校验hutool工具类的具体使用(代码片段)
我是ABin-阿斌:写一生代码,创一世佳话,筑一览芳华。如果小伙伴们觉得文章有点feel,那就点个赞再走哦。声明:原文地址:https://blog.csdn.net/weixin_51216079/article/details/120413239原文作者:CSDN:Coder-C... 查看详情
jsr303标准
JSR303是Java为bean数据合法性校验提供的标准框架,JSR303通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的校验接口对bean进行验证。可用注解的列表如下: 查看详情
使用jsr-303进行校验@valid
一、在SringMVC中使用使用注解1、准备校验时使用的JARvalidation-api-1.0.0.GA.jar:JDK的接口;hibernate-validator-4.2.0.Final.jar是对上述接口的实现;log4j、slf4j、slf4j-log4j 2、编写需要校验的bean@NotNull(message="名字不能为空")privateStringuserName 查看详情
使用jsr-303进行后台数据校验
一、在SringMVC中使用使用注解1、准备校验时使用的JARvalidation-api-1.0.0.GA.jar:JDK的接口;hibernate-validator-4.2.0.Final.jar是对上述接口的实现;log4j、slf4j、slf4j-log4j 2、编写需要校验的bean@NotNull(message="名字不能为空")privateStringuserName 查看详情
谷粒商城学习——p69jsr303分组校验(代码片段)
p67中有个问题没有解决,就是在新增和修改的时候,校验规则可能并不一样,一套校验规则显然不适用。利用jsr303校验分组可解决新建分组interface,必须是interfaceAddGrouppackagecom.atguigu.common.valid;/***@Description:新增时分组的校验,jsr... 查看详情
在 JSF 2 Web 应用程序中使用 Bean Validation (JSR 303) 是个好主意吗?
...aces2创建一个Web应用程序。在后端,使用EJB3.1和JPA2等其他常用JEE技术进行管理。关键是,我遵循一些 查看详情
使用 JSR 303 在 JSF 中使用内联消息进行跨字段验证
】使用JSR303在JSF中使用内联消息进行跨字段验证【英文标题】:CrossFieldValidationwithinlinemessaginginJSFwithJSR303【发布时间】:2011-08-0402:09:17【问题描述】:调用类级别JSR-303约束的最佳方法是什么?这些约束从JSF进行跨字段验证,并... 查看详情
springboot系列jsr-303基于由hibernate-validitor实现的后端服务器数据校验(代码片段)
...e-Validitor实现的后端服务器数据校验1、JSR-303简介2、JSR-303常用校验注解规则3、JSR-303如何使用?3.1、首先导入相关依赖(**规范**和**实现**)3.2、对实体类SystemUserinfo==部分字段==进行校验注解3.3、编写登录... 查看详情
如何在非必填字段 JSR 303 上使用 @Pattern
】如何在非必填字段JSR303上使用@Pattern【英文标题】:Howtouse@Patternonnon-mandatoryfieldsJSR303【发布时间】:2011-08-2507:59:56【问题描述】:如何在非强制性表单字段上使用@Pattern约束?@Pattern(regexp="...")privateStringsomething;只要我提交表单... 查看详情