java对get参数校验失败_Spring参数校验及通用异常信息返回

前言

用注解@Validated、@Valid进行参数验证,相对于以前常用的if等条件,会显得简练很多,而且显得更加优雅。

然后是他俩的区别

@Validated:用在方法入参上无法单独提供嵌套验证功能,不能用在成员属性(字段)上,也无法提示框架进行嵌套验证,能配合嵌套验证注解@Valid进行嵌套验证。

@Valid:用在方法入参上无法单独提供嵌套验证功能,能够用在成员属性(字段)上,提示验证框架进行嵌套验证,能配合嵌套验证注解@Valid进行嵌套验证。

安装Maven依赖

org.springframework.boot

spring-boot-starter-validation

常用的验证注解

@Null 被注释的元素必须为null

@NotNull CharSequence, Collection, Map 和 Array 对象不能是 null, 但可以是空集(size = 0)。

@NotEmpty CharSequence, Collection, Map 和 Array 对象不能是 null 并且相关对象的 size 大于 0。

@NotBlank String 不是 null 且去除两端空白字符后的长度(trimmed length)大于 0。

@AssertTrue 被注释的元素必须为true

@AssertFalse 被注释的元素必须为false

@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Size(max,min) 被注释的元素的大小必须在指定的范围内。

@Digits(integer,fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past 被注释的元素必须是一个过去的日期

@Future 被注释的元素必须是一个将来的日期

@Pattern(value) 被注释的元素必须符合指定的正则表达式。

@Email 被注释的元素必须是电子邮件地址

@Length 被注释的字符串的大小必须在指定的范围内

@Range 被注释的元素必须在合适的范围内

全局异常捕获

ControllerAdvice用于捕获全局的控制器抛出的异常,这里只列出了验证相关的异常,其他Exception或者自定义的异常,都可以在这里捕获。

@RestControllerAdvice

@Slf4j

@Component

public class GlobalExceptionHandler {

/**

* 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常

*

* @param e e

* @return Result

*/

@ExceptionHandler(BindException.class)

@ResponseBody

public String bindExceptionHandler(BindException e) {

log.error("{}, 发生异常: {}", httpServletRequest.getRequestURI(), e);

String message = e.getBindingResult().getAllErrors().stream()

.map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());

return Result.build().errorJson(message);

}

/**

* 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。

*

* @param httpServletRequest httpServletRequest

* @param e e

* @return Result

*/

@ExceptionHandler(MethodArgumentNotValidException.class)

@ResponseBody

public String exceptionHandler(HttpServletRequest httpServletRequest, MethodArgumentNotValidException e) {

log.error("{}, 发生异常: {}", httpServletRequest.getRequestURI(), e);

String errField = Objects.nonNull(e.getBindingResult().getFieldError()) ? ":" + e.getBindingResult().getFieldError().getField() : "";

Object rejectValue = e.getBindingResult().getFieldError().getRejectedValue();

return Result.build().errorJson(StrUtil.format("字段{}不允许值:{}", errField, rejectValue));

}

/**

* 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException

*

* @param httpServletRequest httpServletRequest

* @param e e

* @return Result

*/

@ExceptionHandler(ConstraintViolationException.class)

@ResponseBody

public String exceptionHandler(HttpServletRequest httpServletRequest, ConstraintViolationException e) {

log.error("{}, 发生异常: {}", httpServletRequest.getRequestURI(), e);

StringBuilder sb = new StringBuilder();

e.getConstraintViolations().forEach(constraintViolation -> {

sb.append(StrUtil.format("字段:{},不允许值:{},{} ", constraintViolation.getPropertyPath(),

constraintViolation.getInvalidValue(), constraintViolation.getMessage()));

});

return Result.build().errorJson(sb.toString().trim());

}

}

如何使用

比如新建这样一个类,给对应的字段添加注解以及设置message参数,如果不设置message,会使用默认的错误消息

public class User {

@NotEmpty(message = "请输入用户名")

private String username;

@NotEmpty

@Length(min=6,max=18,message = "密码在6位到18位之间")

private String password;

}

如果验证不符合会自动抛出异常,由上面的exceptionHandler进行捕获处理。

针对方法的普通变量参数进行验证时,@Validated需要加在控制器上,然后变量前加验证注解

这个如果自动抛出异常的话时ConstraintViolationException

@RestController

@RequestMapping("/file")

@Validated

public class FileController{

@DeleteMapping("/deleteFile")

public Result> deleteFile( @NotBlank String jobId,@NotBlank String filePath) throws IOException {

return jobService.deleteFile(jobId, filePath);

}

}

{

"code": "1",

"data": {},

"msg": "操作失败:字段:deleteFile.jobId,不允许值:null,不能为空 字段:deleteFile.filePath,不允许值:null,不能为空"

}

用Validated或Valid注解,验证一个实体类,也就是接收json数据,这个需要将注解标注于变量前面

这个如果自动抛出异常的话时MethodArgumentNotValidException

@PostMapping

public Result> add(@RequestBody @Validated JobDO jobDO) {

return Result.build(jobService.insertOrUpdate(jobDO)).success();

}

@PostMapping

public Result> add(@RequestBody @Valid JobDO jobDO) {

return Result.build(jobService.insertOrUpdate(jobDO)).success();

}

{

"code": "1",

"data": {},

"msg": "操作失败:字段:fileId,不允许值:null,不能为空"

}

嵌套校验

对象的嵌套校验,需要在嵌套的字段上添加@Valid注解,否则验证时会略过字段props所属类Item里字段的验证规则

public class SelectList {

@NotNull(message = "id不能为空")

@Min(value = 1, message = "id必须为正整数")

private Long id;

@NotNull(message = "props不能为空")

@Size(min = 1, message = "至少要有一个属性")

@Valid // 嵌套验证必须用@Valid

private List props;

}

public class Item {

@NotBlank(message = "name不能为空")

private String name;

@NotBlank(message = "value不能为空")

private String value;

}

分组验证

Validation的分组验证,比如添加/编辑数据时,添加是不需要验证id的,而编辑的话id是必须的,还有可能这个类被不同控制器,方法使用时需要的参数不同,这时可以指定其groups参数指定要验证的规则。

//用于声明验证规则所属的分组

public interface EditSelectListGroup

{

}

public class SelectList {

//指定验证规则属于哪个分组

@NotNull(message = "id不能为空",groups = {EditSelectListGroup.class})

@Min(value = 1, message = "id必须为正整数" ,groups= {EditSelectListGroup.class})

private Long id;

@NotNull(message = "props不能为空")

@Size(min = 1, message = "至少要有一个属性")

private List props;

}

//指定使用EditSelectListGroup分组下的验证规则,如果不指定分组,那么SelectList的字段id上注解@NotNull和@Min会被忽略

//此处必须使用@Validated而不是@Valid,因为@Valid不支持分组条件

@PutMapping

public Result> edit(@RequestBody @Validated(value = {EditSelectListGroup.class}) JobDO jobDO) {

//xxxxxxxxxxxxxxxxxxxxxxx

}

数据传递到spring中的执行过程

前端通过HTTP协议将数据传递到Spring,Spring通过HttpMessageConverter类将流数据转换成Map类型,然后通过ModelAttributeMethodProcessor类对参数进行绑定到方法对象中,并对带有@Valid或@Validated注解的参数进行参数校验,对参数进行处理和校验的方法为ModelAttributeMethodProcessor.resolveArgument(…),通过查看源码,当BindingResult中存在错误信息时,会抛出BindException异常,BindException实现了BindingResult接口(BindResult是绑定结果的通用接口, BindResult继承于Errors接口),所以该异常类拥有BindingResult所有的相关信息,因此我们可以通过捕获该异常类,对其错误结果进行分析和处理。这样,我们对是Content-Type为application/x-www-form-urlencoded的请求(也就是表单),的参数校验的异常处理就解决了。

对于不同的传输数据的格式spring采用不同的HttpMessageConverter(http参数转换器)来进行处理,比如JasksonHttpMessageConverter,或者使用fastjson的话,可以自定义FastJsonHttpMessageConverter

HttpMessageConverter简介

HTTP 请求和响应的传输是字节流,意味着浏览器和服务器通过字节流进行通信。但是使用Spring MVC的Controller 类中的方法都是返回String类型或其他Java对象,如何将对象转换成字节流进行传输?这时就需要一个消息转化器。

在报文到达Spring MVC和从Spring MVC出去,都存在一个字节流到Java对象的转换问题。在Spring MVC中,它是由HttpMessageConverter来处理的。

当请求报文来到java中,它会被封装成为一个ServletInputStream的输入流,供我们读取报文。响应报文则是通过一个ServletOutputStream的输出流,来输出响应报文。

针对不同的数据格式,Spring MVC会采用不同的消息转换器进行处理,当使用json作为传输格式时,Spring MVC会采用MappingJacksonHttpMessageConverter消息转换器, 而且底层在对参数进行校验错误时,抛出的是MethodArgumentNotValidException异常,因此我们需要对BindException和MethodArgumentNotValidException进行统一异常管理,最终代码演示如上所示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值