本文共 12013 字,大约阅读时间需要 40 分钟。
在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有两个麻烦:
hibernate validator()提供了一套比较完善、便捷的验证实现方式。
spring-boot-starter-web
包里面有hibernate-validator
包,不需要引用hibernate validator依赖。
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; }}
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 { }}
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(); } }}
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; }}
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(); }}
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); }
输入一个空的名子进行测试,可以看到返回结果
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
方法将异常写到前台。
参考
转载地址:http://bikra.baihongyu.com/