简易验证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