Spring Validation框架+AOP实现Control
项目开发过程中,通常都涉及到表单提交时候前台传递的表单数据的数据合法性校验,这里说的合法性指的是数据合法性,不涉及业务逻辑上的合法性。为了避免在每个方法中都去写重复的校验逻辑,基于这个目的,使用了spring自带的validation校验框架+aop实现了一个通用的的入参校验处理方法。
这里实现基于springboot1.5.18.RELEASE,java 1.8,项目结构如下:
pom文件引入的依赖:
创建实体类:
import javax.validation.constraints.Max;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
public class User {
private String id;
//校验字符串是否在指定的范围内
@Length(min=0, max=20,message = "名字最大长度20")
private String name;
@Length(min=0, max=100,message = "地址最大长度100")
private String address;
//email校验,不符合格式则直接返回,为空不校验
@Email(message = "邮箱格式不正确")
private String email;
//年龄最大值
@Max(value = 150,message = "年龄超过最大值150")
private Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", address=" + address + ", email=" + email + ", age=" + age + "]";
}
}
自定义注解,用来创建切面时使用:
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.METHOD)
public @interface ParamValidate {
}
创建切面类:
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import cst.constant.ResponseConstant;
@Aspect
@Component
public class ParamValidateAspect {
//使用java简单日志门面slf4j,springboot默认使用logback
private static final Logger log = LoggerFactory.getLogger(ParamValidateAspect.class);
//定义切面要切的方法为所有的带这个注解的方法
@Pointcut("@annotation(cst.annotation.ParamValidate)")
public void paramValidate() {}
//切面逻辑
@Before("paramValidate()")
public void paramValidateBefore(JoinPoint point) {
//获取方法入参的数据
Object[] paramObjs = point.getArgs();
StringBuffer buffer = new StringBuffer();
//如果入参个数不为0
if (paramObjs.length > 0) {
for (Object object : paramObjs) {
//如果是BindingResult类型的参数
if (object instanceof BindingResult) {
BindingResult result = (BindingResult)object;
//如果有校验失败的信息
if (result.hasErrors()) {
//循环拼接所有的错误信息
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError error : allErrors) {
buffer.append(error.getDefaultMessage()+";");
}
}
}
}
}
//如果校验信息不为空,则直接返回请求
String checkResult = buffer.toString();
if (!StringUtils.isEmpty(checkResult)) {
//获取到HttpServletResponse对象
ServletRequestAttributes res = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = res.getResponse();
//设置编码格式
response.setCharacterEncoding("UTF-8");
//设置应答头类型
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
//根据实际情况拼接要返回的json字符串,这里因为返回使用了自定义的ReturnData实体,所以拼接成这种实体的json格式
String returnData = "{" +
"\"code\":" + "\""+ ResponseConstant.FAILURE_CODE+ "\"" + "," +
"\"msg\":" + "\""+ checkResult + "\"" + "," +
"\"data\":" + "\""+ checkResult + "\""+"," +
"\"pages\": null" +
"}";
//将请求返回
OutputStream output = null;
try {
output = response.getOutputStream();
output.write(returnData.getBytes("UTF-8"));
} catch (Exception e) {
log.error(e.getMessage());
} finally {
try {
if (output != null) {
output.close();
}
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
}
}
要使用的返回值的统一实体和返回值常量类:
/**
* 返回到前台的信息实体
* @author Administrator
*
* @param <T>
*/
public class ReturnData<T> {
//状态码,0为请求成功,其他均失败
private Integer code;
//返回的附加信息
private String msg;
//要返回的数据
private T data;
private Integer pages;
//无参构造
public ReturnData() {}
//有参构造
public ReturnData(int code,String msg,T t){
this.code=code;
this.msg=msg;
this.data=t;
}
public ReturnData(int code,String msg,T t,int pages){
this.code=code;
this.msg=msg;
this.data=t;
this.pages=pages;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Integer getPages() {
return pages;
}
public void setPages(Integer pages) {
this.pages = pages;
}
@Override
public String toString() {
return "ReturnData [code=" + code + ", msg=" + msg + ", data=" + data + ", pages=" + pages + "]";
}
}
/**
* 返回到前台信息的实体常量类
*/
public class ResponseConstant {
//请求成功的code
public static final Integer SUCCESS_CODE=0;
//请求失败的code
public static final Integer FAILURE_CODE=1;
//请求成功的msg
public static final String SUCCESS_MESSAGE="操作成功!";
//请求失败的msg
public static final String FAILURE_MESSAGE="操作失败!";
}
创建的测试controller:
import javax.validation.Valid;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cst.annotation.ParamValidate;
import cst.constant.ResponseConstant;
import cst.entity.ReturnData;
import cst.entity.User;
@RestController
@RequestMapping(value = "/user")
public class UserController {
@ParamValidate//这个方法上定义这个注解,切面注解
@RequestMapping(value = "/saveUser")
//这里注意使用@Valid 和 BindingResult,这有对应关系
public ReturnData<String> saveUser(@Valid @RequestBody User user,BindingResult result) {
return new ReturnData<String>(ResponseConstant.SUCCESS_CODE,ResponseConstant.SUCCESS_MESSAGE,ResponseConstant.SUCCESS_MESSAGE);
}
}
使用postman模拟表单提交进行测试,请求头设置:
请求返回结果:
常见的注解如下,基本满足需求:
JSR提供的校验注解:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator提供的校验注解:
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
以上是一种最简单的实现思路,具体可以根据自己需求进一步优化。