博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
springboot统一表单数据校验
阅读量:6412 次
发布时间:2019-06-23

本文共 12013 字,大约阅读时间需要 40 分钟。

统一表单数据校验

在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有两个麻烦:

  • 验证代码繁琐,重复劳动
  • 方法内代码显得冗长
  • 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码

hibernate validator()提供了一套比较完善、便捷的验证实现方式。

spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。

于是编写了一套校验工具类,并配置了切面做统一的校验,无需在每次手动调用校验方法。

校验工具类 ValidatorUtil

import javax.validation.ConstraintViolation;import javax.validation.Validation;import javax.validation.Validator;import javax.validation.groups.Default;import java.util.ArrayList;import java.util.List;import java.util.Set;/** * @author lism * @date 2018年8月29日10:25:34 * bean 校验工具类 */public class ValidatorUtil {    private static Validator validator = Validation.buildDefaultValidatorFactory()            .getValidator();    /**     * 校验bean     * @param bean     * @param 
* @return */ public static
List
validate(T bean) { Set
> constraintViolations = validator.validate(bean, Default.class); return errors2ValidateBeanList(constraintViolations); } /** * 校验属性 * @param bean * @param property * @param
* @return */ public static
List
validateProperty(T bean, String property) { Set
> constraintViolations = validator.validateProperty(bean, property, Default.class); return errors2ValidateBeanList(constraintViolations); } /** * 校验属性值 * @param bean * @param property * @param propertyValue * @param
* @return */ public static
List
validateValue(T bean, String property, Object propertyValue) { Set
> constraintViolations = validator.validateValue(bean.getClass(), property, propertyValue, Default.class); return errors2ValidateBeanList(constraintViolations); } private static
List
errors2ValidateBeanList(Set
> errors) { List
validateBeans = new ArrayList<>(); if (errors != null && errors.size() > 0) { for (ConstraintViolation
cv : errors) { //这里循环获取错误信息,可以自定义格式 String property = cv.getPropertyPath().toString(); String message = cv.getMessage(); validateBeans.add(new ValidateBean(property, message)); } } return validateBeans; }}

校验结果辅助类 ValidateBean

import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@NoArgsConstructor@AllArgsConstructorpublic class ValidateBean {    private String property;    private String message;    @Override    public String toString() {        final StringBuilder sb = new StringBuilder("{");        sb.append("property='").append(property).append('\'');        sb.append(", message='").append(message).append('\'');        sb.append('}');        return sb.toString();    }}

统一数据校验和异常处理切面

import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Arrays;import java.util.List;import java.util.Optional;/** * 统一数据校验和异常处理 * 控制层无需再进行数据校验和异常捕获 * @author lism */@Aspect@Component@Slf4j@Order(1)public class ValidateBeanAspect {    /**     * 定义一个切入点     */    @Pointcut("execution(* org.lism..controller..*.*(..))")    private void anyMethod() {    }    @Around("anyMethod()")    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {        Object[] args = pjp.getArgs();        if (args.length > 0) {            Optional
> optional = Arrays.stream(args).filter(arg -> { return !(arg == null ||arg instanceof HttpServletRequest || arg instanceof HttpServletResponse); }).map(arg -> { return ValidatorUtil.validate(arg); }).filter(validateBeans -> { return validateBeans.size() > 0; }).findFirst(); if (optional.isPresent()) { return new ResponseBean(RTCodeEnum.CODE_FAIL.getCode(), optional.get().toString()); } } try { Object proceed = pjp.proceed(args); if (proceed instanceof ResponseBean) { return proceed; } else { return new ResponseBean(proceed, RTCodeEnum.CODE_200); } } catch (BaseException e) { RequestContextUtil.writeToResponse(new ResponseBean<>(e.getCode(), e.getMessage()).toString()); log.error(e.getMessage());// return new ResponseBean<>(e.getCode(), e.getMessage()); return null; } catch (Exception e) { log.error(e.getMessage()); RequestContextUtil.writeToResponse(new ResponseBean<>(RTCodeEnum.CODE_FAIL.getCode(), e.getMessage()));// return new ResponseBean<>(RTCodeEnum.CODE_FAIL.getCode(), e.getMessage()); return null; } } @Before("anyMethod()") public void doBefore(JoinPoint pjp) throws Throwable { } @AfterReturning("anyMethod()") public void doAfterReturning(JoinPoint pjp) throws Throwable { }}

RequestContextUtil工具类

import com.alibaba.fastjson.JSON;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;/** * RequestContext工具类 * @Author lism * @Date 2018/8/29 17:04 */public class RequestContextUtil {    public static ServletRequestAttributes getRequestAttributes() {        return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();    }    /**     * 获取Request     * @return     */    public static HttpServletRequest getRequest() {        //TODO:单元测试的时候,还是会得到requestAttributes,不等于null        ServletRequestAttributes requestAttributes = getRequestAttributes();        if (requestAttributes != null) {            return requestAttributes.getRequest();        } else {            return null;        }    }    /**     * 获取Response     * @return     */    public static HttpServletResponse getResponse() {        //TODO:单元测试的时候,还是会得到requestAttributes,不等于null        ServletRequestAttributes requestAttributes = getRequestAttributes();        if (requestAttributes != null) {            return requestAttributes.getResponse();        } else {            return null;        }    }    /**     * 获取SessionId     * @return     */    public static String getSessionId() {        //TODO:单元测试的时候,还是会得到requestAttributes,不等于null        ServletRequestAttributes requestAttributes = getRequestAttributes();        if (requestAttributes != null) {            return requestAttributes.getSessionId();        } else {            return null;        }    }    /**     * 往前端写数据     * @param object     */    public static void writeToResponse(Object object) {        PrintWriter writer = null;        try {            HttpServletResponse response = RequestContextUtil.getResponse();            response.setCharacterEncoding("utf-8");            response.setHeader("Content-type", "text/html;charset=utf-8");            writer = response.getWriter();            writer.write(JSON.toJSONString(object));            writer.flush();        } catch (IOException e) {            e.printStackTrace();        }finally {            writer.close();        }    }}

返回code 枚举类

import com.alibaba.fastjson.JSONObject;/** */public enum RTCodeEnum {    CODE_OK(0, "OK"), //    CODE_DONE(1, "Done"), //    CODE_FAIL(-1, "Failed"),    CODE_PARAM_ERROR(300, "Input Param Error"),    CODE_TOKEN_ERROR(301, "Token Validation Error"),    CODE_CAPTCHA_ERROR(302, "验证码错误,请重试"),    CODE_DATA_VALIDATE_FAILED(303, "数据校验未通过"),    CODE_STATE_EXIST(305, "请勿重复请求"),    // Data Issue: 4**    CODE_400(400, "服务404"),    CODE_DATA_ERROR_PAGETIME_EXPIRE(401, "页面超时不可用,请刷新重试"),    // System Service Issue: 5**    CODE_SERVICE_NOT_AVAILABLE(500, "系统服务不可用,请联系管理员"),    CODE_200(200,"成功"),    CODE_401(401,"未登录,需要登录"),    CODE_405(405, "权限不足"),    CODE_406(406,"客户端请求接口参数不正确或缺少参数"),    CODE_501(501,"服务器接口错误"),    CODE_999(999,"保留码");    private int code;    private String desc;    RTCodeEnum(int code, String desc) {        this.code = code;        this.desc = desc;    }    public JSONObject toJSON() {        JSONObject jsonObject = new JSONObject();        jsonObject.put("code", code);        jsonObject.put("desc", desc);        return jsonObject;    }    public int getCode() {        return code;    }    public void setCode(int code) {        this.code = code;    }    public String getDesc() {        return desc;    }    public void setDesc(String desc) {        this.desc = desc;    }}

ResponseBean工具类

import com.alibaba.fastjson.annotation.JSONField;import com.google.common.base.MoreObjects;import java.util.Date;/** * Created by lism 2018/8/23. */public class ResponseBean
{ private int code = 200; private String msg; private T data; @JSONField(format="yyyy-MM-dd HH:mm:ss") private Date date = new Date(); public static ResponseBean me(Object data){ return new ResponseBean(data); } public ResponseBean(T data) { this.data = data; } public ResponseBean(T data, RTCodeEnum rtCodeEnum) { this.data = data; this.msg = rtCodeEnum.getDesc(); this.code = rtCodeEnum.getCode(); } public ResponseBean(RTCodeEnum rtCodeEnum) { this.msg = rtCodeEnum.getDesc(); this.code = rtCodeEnum.getCode(); } public ResponseBean(T data, int code, String msg) { this.data = data; this.code = code; this.msg = msg; } public ResponseBean(int code, String msg) { this.code = code; this.msg = msg; } public ResponseBean() { } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public T getData() { return data; } public void setData(T data) { this.data = data; } public Date getDate() { return date; } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("code", code) .add("msg", msg) .add("data", data) .toString(); }}

1.校验实体类型参数

编写测试实体

import lombok.Data;import org.hibernate.validator.constraints.Length;import javax.persistence.Entity;import javax.persistence.Table;import javax.validation.constraints.NotEmpty;@Entity@Table(name = "t_subject")@Datapublic class Subject extends BaseEntityModel {    @NotEmpty(message = "专题名不能为空")    @Length(min = 1, max = 20, message = "专题名1-20个字符之间")    private String name;    private String keyword;    private String[] docType;    private String startTime;    private String endTime;}

测试控制器

@RestController@RequestMapping(value = "/subject")public class SubjectController {    @RequestMapping(value = "/", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")    @ResponseBody    @Override    public ResponseBean create(@RequestBody Subject model) {           return super.create(model);    }

输入一个空的名子进行测试,可以看到返回结果

v2.png

2.校验RequestParam 类型请求参数

ValidatorUtil方式无法校验RequestParam 请求方法,在BaseController 加上@Validated注解

@RestController@Validatedpublic class BaseController {}

在子控制器写测试方法

@RestController@RequestMapping(value = "/subject")public class SubjectController extends BaseController {      /**     * test4     * @return     */    @RequestMapping(value = "/test3/", method = RequestMethod.GET, produces = "application/json")    @ResponseBody    public void test3(HttpServletRequest request, HttpServletResponse response,                      @Length(min = 1, max = 20, message = "专题名1-20个字符之间")                      @RequestParam String name) {        log.info("test4");    }

通过测试看到:验证不通过时,抛出了ConstraintViolationException异常,所以我们在ValidateBeanAspect中使用统一的捕获异常是可以捕获到异常,并调用RequestContextUtil.writeToResponse方法将异常写到前台。

测试结果:

v1.png

参考

转载地址:http://bikra.baihongyu.com/

你可能感兴趣的文章
WPF 虚拟键盘
查看>>
储存卡无法打开专家教您怎么数据恢复
查看>>
彼得原理
查看>>
如何利用【百度地图API】,制作房产酒店地图?(下)——结合自己的数据库...
查看>>
[20171113]修改表结构删除列相关问题3.txt
查看>>
特征选择
查看>>
在Winform程序中设置管理员权限及为用户组添加写入权限
查看>>
RTMP直播到FMS中的AAC音频直播
查看>>
多能互补提速 加快我国能源转型和现代能源体系建设
查看>>
B2G编译前的准备
查看>>
jQuery ajax()使用serialize()提交form数据
查看>>
程序框架的作用
查看>>
什么是DHTML
查看>>
Oxite学习之一:Oxite安装
查看>>
extjs4 panel下tools里的元素选择器
查看>>
Mac下使用Docker简单介绍
查看>>
SpringMvc Ehcache 实现缓存机制
查看>>
javascript闭包的使用
查看>>
Backbone.js 使用模板
查看>>
安装xenomai的记实
查看>>