springboot+validation的参数验证(含嵌套验证和全局验证异常捕获)

简易验证Demo:

一。pom.xml添加依赖:

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

若已有“spring-boot-starter-web”,则不必再加“spring-boot-starter-validation”:

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

二。实体类添加校验规则:

package demo.rest;

import lombok.Data;

import javax.validation.constraints.NotNull;

/**
 * description:
 * author: yangzihe
 * date: 2019-05-16 15:50
 **/
@Data
public class User {

    @NotNull(message = "用户名不可为空")
    private String name;

    @NotNull(message = "密码不可为空")
    private String password;

    
}

三。controller层添加验证注解(使用BindingResult接收验证结果):

package demo.rest;

import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * description:
 * author: yangzihe
 * date: 2019-05-16 15:48
 **/
@RestController
public class DemoController {

    @GetMapping("valid")
    public String test1(@Validated User user, BindingResult result) {//@Valid注解也可
        System.out.println(user);
        System.out.println("result=" + result);
        if (result.hasErrors()) {
            StringBuilder sb = new StringBuilder();
            for (ObjectError error : result.getAllErrors()) {
                sb.append(error.getDefaultMessage() + ";");
            }
            System.out.println(sb.toString());
            return sb.toString();
        }
        return "success";
    }

}

四。启动类启动,浏览器访问http://localhost:8080/valid

@org.springframework.boot.autoconfigure.SpringBootApplication
public class SpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootApplication.class, args);
    }

}
User(name=null, password=null)
result=org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'user' on field 'name': rejected value [null]; codes [NotNull.user.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.name,name]; arguments []; default message [name]]; default message [用户名不可为空]
Field error in object 'user' on field 'password': rejected value [null]; codes [NotNull.user.password,NotNull.password,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.password,password]; arguments []; default message [password]]; default message [密码不可为空]
用户名不可为空;密码不可为空;

补充,常用验证注解:
@Null    限制只能为null
@NotNull    限制必须不为null
@AssertFalse    限制必须为false
@AssertTrue    限制必须为true
@DecimalMax(value)    限制必须为一个不大于指定值的数字
@DecimalMin(value)    限制必须为一个不小于指定值的数字
@Digits(integer,fraction)    限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future    限制必须是一个将来的日期
@Max(value)    限制必须为一个不大于指定值的数字
@Min(value)    限制必须为一个不小于指定值的数字
@Past    限制必须是一个过去的日期
@Pattern(value)    限制必须符合指定的正则表达式
@Size(max,min)    限制字符长度必须在min到max之间
@Past    验证注解的元素值(日期类型)比当前时间早
@NotEmpty    验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank    验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email    验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

举例:

 @Pattern(regexp="^[a-zA-Z0-9]+$",message="字段必须为英文或数字")
 @Size(min=1,max=20,message="{username.size}")

message="{username.size}"这种可将message放在resource 目录下提示信息配置文件“ValidationMessages.properties“中(名字必须为“ValidationMessages.properties“ 因为SpringBoot自动读取classpath中的ValidationMessages.properties文件名):

username.size=用户名必须在1到20字符之间

注意:

1.ValidationMessages.properties默认使用ISO8859-1编码的,所以解码得到的字符串是乱码,解决:ValidationMessages.properties文件编码设置为UTF-8,new String(error.getDefaultMessage().getBytes("ISO8859-1"), "UTF-8")。

2.min、max指的是字符长度,不是字节长度。

嵌套验证:

import lombok.Data;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;

/**
 * @description: 
 * @author: yangzihe(ys1405)
 * @create: 2020-02-07 11:21
 **/
@Data
public class TerminalInsertPost {
    @NotBlank(message = "{terminal.groupId}")
    private String groupId;
    @NotBlank(message = "{terminal.terminalType}")
    private String terminalType;
    @Valid
    @NotEmpty(message = "{terminal.list}")
    private List<TerminalIdName> list;

}
import lombok.Data;

import javax.validation.constraints.NotBlank;

/**
 * @description: 
 * @author: yangzihe(ys1405)
 * @create: 2020-02-07 11:21
 **/
@Data
public class TerminalIdName {
    @NotBlank(message = "{terminal.terminalId}")
    private String terminalId;
    @NotBlank(message = "{terminal.terminalName}")
    private String terminalName;

}

ValidationMessages.properties文件内容:

terminal.groupId=组ID不可为空
terminal.terminalType=终端类型不可为空
terminal.terminalId=终端ID不可为空
terminal.terminalName=终端名称不可为空
terminal.list=终端集合不可为空

嵌套验证的关键就是在嵌套对象上添加“@Valid”注解,使嵌套对象的验证生效。

全局验证异常捕获:

上面的简易验证Demo是在controller层的方法中接收BindingResult对象,判断对象是否有验证错误从而进行了参数的验证。然而,每个controller层方法都执行一次,就很累赘了,这时候就需要提取一下验证异常的处理了。

@RestController
@RequestMapping("/terminal")
public class TerminalController {
  
    @PutMapping("/insert")
    public String insertPut(@RequestBody @Validated TerminalInsertPost terminalReq) {
        
        return "success";
    }

}
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 参数验证异常(前端json格式提交)
     *
     * @param e 异常
     * @return 响应实体
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String handleParameterException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        String errorMessage = CommonUtil.getErrorMessage(e.getBindingResult());
        return errorMessage;
    }

    /**
     * 参数验证异常(前端form表单格式提交)
     *
     * @param exception 异常
     * @return http响应对象
     */
    @ExceptionHandler(BindException.class)
    public String handlerBindException(BindException exception) {
        String errorMessage = CommonUtil.getErrorMessage(exception.getBindingResult());
        return errorMessage;
    }

}
public final class CommonUtil {

    private CommonUtil() {
    }

    /**
     * 获取参数验证的结果
     *
     * @param bindingResult 参数验证的绑定结果
     * @return 验证结果
     */
    public static String getErrorMessage(BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            StringBuilder sb = new StringBuilder();
            for (ObjectError error : bindingResult.getAllErrors()) {
                try {
                    sb.append(new String(error.getDefaultMessage().getBytes("ISO8859-1"), "UTF-8") + ";");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    return null;
                }
            }
            return sb.toString();
        }
        return null;
    }

    
}

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

对于不同的传输数据的格式spring采用不同的HttpMessageConverter(http参数转换器)来进行处理.以下是对HttpMessageConverter进行简介:

HTTP 请求和响应的传输是字节流,意味着浏览器和服务器通过字节流进行通信。但是,使用 Spring,controller 类中的方法返回纯 String 类型或其他 Java 内建对象。如何将对象转换成字节流进行传输?

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

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

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


 

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`javax.validation.ConstraintViolationException` 是 Java Bean Validation 规范中定义的异常,表示数据校验失败。当使用注解对方法参数进行校验时,如果参数不符合校验规则,则会抛出 `javax.validation.ConstraintViolationException` 异常。 一般情况下,我们需要在代码中对该异常进行捕获和处理,并将错误信息返回给客户端。以下是一个使用 `@ExceptionHandler` 注解捕获和处理 `javax.validation.ConstraintViolationException` 异常的例子: ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(javax.validation.ConstraintViolationException.class) @ResponseBody public ResponseEntity<String> handleValidationException(javax.validation.ConstraintViolationException ex) { Set<ConstraintViolation<?>> violations = ex.getConstraintViolations(); StringBuilder sb = new StringBuilder(); for (ConstraintViolation<?> violation : violations) { sb.append(violation.getMessage()).append("\n"); } return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString()); } } ``` 在上述代码中,我们使用 `@ExceptionHandler` 注解捕获 `javax.validation.ConstraintViolationException` 异常,并通过 `ex.getConstraintViolations()` 方法获取到所有的校验错误信息。然后将错误信息构造成一个字符串返回给客户端。 需要注意的是,使用 `@ControllerAdvice` 注解可以将异常处理器作用于所有的控制器中。而 `@ExceptionHandler` 注解则用于标识具体的异常处理方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值